Merge branch 'master' into dev/auklet

Change-Id: I2d4ce97786694a9032c87a41dfca8ee72786efab
diff --git a/apps/acl/src/main/java/org/onosproject/acl/AclRule.java b/apps/acl/src/main/java/org/onosproject/acl/AclRule.java
index a5cc297..ff01aa4 100644
--- a/apps/acl/src/main/java/org/onosproject/acl/AclRule.java
+++ b/apps/acl/src/main/java/org/onosproject/acl/AclRule.java
@@ -43,7 +43,7 @@
     private final short dstTpPort;
     private final Action action;
 
-    private static IdGenerator idGenerator;
+    protected static IdGenerator idGenerator;
 
     /**
      * Enum type for ACL rule's action.
diff --git a/apps/acl/src/test/java/org/onosproject/acl/AclWebResourceTest.java b/apps/acl/src/test/java/org/onosproject/acl/AclWebResourceTest.java
index 13c8201..003cf8c 100644
--- a/apps/acl/src/test/java/org/onosproject/acl/AclWebResourceTest.java
+++ b/apps/acl/src/test/java/org/onosproject/acl/AclWebResourceTest.java
@@ -27,6 +27,7 @@
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
+import org.onlab.junit.TestUtils;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
 import org.onlab.rest.BaseResource;
@@ -65,10 +66,9 @@
         ServiceDirectory testDirectory = new TestServiceDirectory()
                 .add(AclService.class, mockAclService)
                 .add(AclStore.class, mockAclStore);
-        BaseResource.setServiceDirectory(testDirectory);
+        TestUtils.setField(BaseResource.class, "services", testDirectory);
 
-        IdGenerator idGenerator = new MockIdGenerator();
-        AclRule.bindIdGenerator(idGenerator);
+        AclRule.idGenerator = new MockIdGenerator();
     }
 
     @After
diff --git a/apps/actn-mdsc/tetunnel-pce/src/main/java/org/onosproject/actn/mdsc/pce/impl/TeTunnelPceManager.java b/apps/actn-mdsc/tetunnel-pce/src/main/java/org/onosproject/actn/mdsc/pce/impl/TeTunnelPceManager.java
index 890c7ba..b0efe80 100644
--- a/apps/actn-mdsc/tetunnel-pce/src/main/java/org/onosproject/actn/mdsc/pce/impl/TeTunnelPceManager.java
+++ b/apps/actn-mdsc/tetunnel-pce/src/main/java/org/onosproject/actn/mdsc/pce/impl/TeTunnelPceManager.java
@@ -16,6 +16,7 @@
 
 package org.onosproject.actn.mdsc.pce.impl;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -63,7 +64,11 @@
                 }
             }
         }
-        return pce.computePaths(teTunnel);
+        if (pce != null) {
+            return pce.computePaths(teTunnel);
+        } else {
+            return ImmutableList.of();
+        }
     }
 
     @Override
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMdListMdCommand.java b/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMdListMdCommand.java
index 341bd7d..f3c5b6e 100644
--- a/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMdListMdCommand.java
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMdListMdCommand.java
@@ -20,6 +20,12 @@
 import org.onosproject.cli.AbstractShellCommand;
 import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceAssociation;
 import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceDomain;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaId2Octet;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdIccY1731;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdPrimaryVid;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdRfc2685VpnId;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort;
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId;
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr;
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdDomainName;
@@ -45,29 +51,7 @@
         CfmMdService service = get(CfmMdService.class);
 
         if (name != null) {
-            String[] nameParts = name.split("[()]");
-            if (nameParts.length != 2) {
-                throw new IllegalArgumentException("Invalid name format. " +
-                        "Must be in the format of <identifier(name-type)>");
-            }
-
-            MdId mdId = null;
-            MdId.MdNameType nameTypeEnum = MdId.MdNameType.valueOf(nameParts[1]);
-            switch (nameTypeEnum) {
-                case DOMAINNAME:
-                    mdId = MdIdDomainName.asMdId(nameParts[0]);
-                    break;
-                case MACANDUINT:
-                    mdId = MdIdMacUint.asMdId(nameParts[0]);
-                    break;
-                case NONE:
-                    mdId = MdIdNone.asMdId();
-                    break;
-                case CHARACTERSTRING:
-                default:
-                    mdId = MdIdCharStr.asMdId(nameParts[0]);
-            }
-
+            MdId mdId = parseMdName(name);
             print("Maintenance Domain:");
             Optional<MaintenanceDomain> md = service.getMaintenanceDomain(mdId);
             print(printMd(md));
@@ -131,4 +115,59 @@
 
         return sb.toString();
     }
+
+    public static MdId parseMdName(String mdStr) {
+        String[] nameParts = mdStr.split("[()]");
+        if (nameParts.length != 2) {
+            throw new IllegalArgumentException("Invalid name format. " +
+                    "Must be in the format of <identifier(name-type)>");
+        }
+
+        MdId mdId = null;
+        MdId.MdNameType nameTypeEnum = MdId.MdNameType.valueOf(nameParts[1]);
+        switch (nameTypeEnum) {
+            case DOMAINNAME:
+                mdId = MdIdDomainName.asMdId(nameParts[0]);
+                break;
+            case MACANDUINT:
+                mdId = MdIdMacUint.asMdId(nameParts[0]);
+                break;
+            case NONE:
+                mdId = MdIdNone.asMdId();
+                break;
+            case CHARACTERSTRING:
+            default:
+                mdId = MdIdCharStr.asMdId(nameParts[0]);
+        }
+        return mdId;
+    }
+
+    public static MaIdShort parseMaName(String maStr) {
+        String[] nameParts = maStr.split("[()]");
+        if (nameParts.length != 2) {
+            throw new IllegalArgumentException("Invalid name format. " +
+                    "Must be in the format of <identifier(name-type)>");
+        }
+
+        MaIdShort maId = null;
+        MaIdShort.MaIdType nameTypeEnum = MaIdShort.MaIdType.valueOf(nameParts[1]);
+        switch (nameTypeEnum) {
+            case ICCY1731:
+                maId = MaIdIccY1731.asMaId(nameParts[0]);
+                break;
+            case PRIMARYVID:
+                maId = MaIdPrimaryVid.asMaId(nameParts[0]);
+                break;
+            case RFC2685VPNID:
+                maId = MaIdRfc2685VpnId.asMaIdHex(nameParts[0]);
+                break;
+            case TWOOCTET:
+                maId = MaId2Octet.asMaId(nameParts[0]);
+            case CHARACTERSTRING:
+            default:
+                maId = MaIdCharStr.asMaId(nameParts[0]);
+        }
+        return maId;
+    }
+
 }
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMepListCommand.java b/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMepListCommand.java
new file mode 100644
index 0000000..b406947
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMepListCommand.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2018-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.cfm.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.incubator.net.l2monitoring.cfm.MepEntry;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepKeyId;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMdService;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepService;
+import org.slf4j.Logger;
+
+import static org.onosproject.cfm.cli.CfmMdListMdCommand.parseMaName;
+import static org.onosproject.cfm.cli.CfmMdListMdCommand.parseMdName;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Lists a particular Maintenance Domain.
+ */
+@Command(scope = "onos", name = "cfm-mep-list",
+        description = "Lists a filtered set of MEPs or all if no parameters specified.")
+public class CfmMepListCommand extends AbstractShellCommand {
+    private final Logger log = getLogger(getClass());
+    @Argument(index = 0, name = "md",
+            description = "Maintenance Domain name and type (in brackets) - will use all MDs if not specified",
+            required = false, multiValued = false)
+    String mdStr = null;
+    @Argument(index = 1, name = "ma",
+            description = "Maintenance Association name and type (in brackets) - requires MD",
+            required = false, multiValued = false)
+    String maStr = null;
+    @Argument(index = 2, name = "mep",
+            description = "MEP identifier - requires MD and MA",
+            required = false, multiValued = false)
+    String mepStr = null;
+
+    @Override
+    protected void execute() {
+        CfmMepService mepService = get(CfmMepService.class);
+        CfmMdService mdService = get(CfmMdService.class);
+
+        if (mdStr != null && !mdStr.isEmpty()) {
+
+
+            MdId mdId = parseMdName(mdStr);
+            print(printMdId(mdId));
+
+            if (maStr != null && !maStr.isEmpty()) {
+                MaIdShort maId = parseMaName(maStr);
+                print(printMaId(maId));
+
+                if (mepStr != null && !mepStr.isEmpty()) {
+                    MepId mepId = MepId.valueOf(Short.parseShort(mepStr));
+                    try {
+                        MepEntry mep = mepService.getMep(mdId, maId, mepId);
+                        if (mep != null) {
+                            print(printMepEntry(mep));
+                        }
+                    } catch (CfmConfigException e) {
+                        log.error("Error retrieving Mep details {}",
+                                new MepKeyId(mdId, maId, mepId), e);
+                    }
+
+                    //MD, MA and MEP given
+                } else {
+                    //MD and MA given but no MEP given
+                    try {
+                        mepService.getAllMeps(mdId, maId).forEach(mep -> {
+                            print(printMepEntry(mep));
+                        });
+                    } catch (CfmConfigException e) {
+                        log.error("Error retrieving Meps for {}/{}",
+                                mdId.mdName(), maId.maName(), e);
+                    }
+                }
+            } else {
+                //MD given but no MA given
+                mdService.getAllMaintenanceAssociation(mdId).forEach(ma -> {
+                    print(printMaId(ma.maId()));
+                    try {
+                        mepService.getAllMeps(mdId, ma.maId()).forEach(mep -> {
+                            print(printMepEntry(mep));
+                        });
+                    } catch (CfmConfigException e) {
+                        log.error("Error retrieving Meps for {}/{}",
+                                mdId.mdName(), ma.maId().maName(), e);
+                    }
+                });
+
+            }
+        } else {
+            mdService.getAllMaintenanceDomain().forEach(md -> {
+                print(printMdId(md.mdId()));
+
+                mdService.getAllMaintenanceAssociation(md.mdId()).forEach(ma -> {
+                    print(printMaId(ma.maId()));
+                    try {
+                        mepService.getAllMeps(md.mdId(), ma.maId()).forEach(mep -> {
+                            print(printMepEntry(mep));
+                        });
+                    } catch (CfmConfigException e) {
+                        log.error("Error retrieving Meps for {}/{}",
+                                md.mdId().mdName(), ma.maId().maName(), e);
+                    }
+                });
+            });
+        }
+    }
+
+    /**
+     * Print the whole MEP Entry (config and status).
+     * @param mep The MEPEntry to print
+     * @return A string with MepEntry details
+     */
+    public static String printMepEntry(MepEntry mep) {
+        StringBuffer sb = new StringBuffer("MEP: ");
+        sb.append(mep.mepId());
+        sb.append(" Device:" + mep.deviceId());
+        sb.append(", Port: " + mep.port());
+        sb.append(", Vlan: " + mep.primaryVid());
+        sb.append(", AdminSt: " + mep.administrativeState());
+        sb.append(", CciEnabled: " + mep.cciEnabled());
+        sb.append(", Priority: " + mep.ccmLtmPriority());
+        sb.append("\n"); //The following are state
+        sb.append(", Total CCMs: " + mep.totalCcmsTransmitted());
+        sb.append(", MAC: " + mep.macAddress());
+        sb.append(", Fault: " + mep.fngState());
+
+        mep.activeRemoteMepList().forEach(rmep -> {
+            sb.append("\n\tRmep: " + rmep.remoteMepId());
+            sb.append(", Mac: " + rmep.macAddress());
+            sb.append(", State: " + rmep.state());
+            sb.append(", Failed Time: " + rmep.failedOrOkTime());
+
+        });
+
+
+        return sb.toString();
+    }
+
+    public static String printMdId(MdId mdId) {
+        StringBuffer sb = new StringBuffer("MD: ");
+        sb.append(mdId.mdName());
+        sb.append("(" + mdId.nameType() + ")");
+        return sb.toString();
+    }
+
+    public static String printMaId(MaIdShort maId) {
+        StringBuffer sb = new StringBuffer("MA: ");
+        sb.append(maId.maName());
+        sb.append("(" + maId.nameType() + ")");
+        return sb.toString();
+    }
+
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMepListDeviceCommand.java b/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMepListDeviceCommand.java
new file mode 100644
index 0000000..c99399a
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMepListDeviceCommand.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2018-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.cfm.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.incubator.net.l2monitoring.cfm.Mep;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepService;
+import org.onosproject.net.DeviceId;
+import org.slf4j.Logger;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Lists all the MEPs on a particular device.
+ */
+@Command(scope = "onos", name = "cfm-mep-device-list",
+        description = "Lists a set of MEPs filtered by device.")
+public class CfmMepListDeviceCommand extends AbstractShellCommand {
+    private final Logger log = getLogger(getClass());
+    @Argument(index = 0, name = "device",
+            description = "Device Id",
+            required = true, multiValued = false)
+    String deviceStr = null;
+
+    @Override
+    protected void execute() {
+        CfmMepService mepService = get(CfmMepService.class);
+        if (deviceStr != null) {
+            DeviceId deviceId = DeviceId.deviceId(deviceStr);
+            try {
+                mepService.getAllMepsByDevice(deviceId).forEach(mep -> {
+                    print(printMep(mep));
+                });
+            } catch (CfmConfigException e) {
+                log.error("Error retrieving Meps for Device {}",
+                        deviceId, e);
+            }
+        }
+    }
+
+    /**
+     * Print only the config part of the MEP.
+     * @param mep The MEP to print
+     * @return A string with MD name, MA name and Mep details
+     */
+    public static String printMep(Mep mep) {
+        StringBuffer sb = new StringBuffer("MEP: ");
+        sb.append(mep.mdId().mdName() + "/");
+        sb.append(mep.maId().maName() + "/");
+        sb.append(mep.mepId());
+        sb.append(" Device:" + mep.deviceId());
+        sb.append(", Port: " + mep.port());
+        sb.append(", Vlan: " + mep.primaryVid());
+        sb.append(", AdminSt: " + mep.administrativeState());
+        sb.append(", CciEnabled: " + mep.cciEnabled());
+        sb.append(", Priority: " + mep.ccmLtmPriority());
+
+        return sb.toString();
+    }
+
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmDeviceIdCompleter.java b/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmDeviceIdCompleter.java
new file mode 100644
index 0000000..6e2e29f
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmDeviceIdCompleter.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2018-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.cfm.cli.completer;
+
+import org.apache.karaf.shell.console.Completer;
+import org.apache.karaf.shell.console.completer.StringsCompleter;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepProgrammable;
+import org.onosproject.net.Device;
+import org.onosproject.net.device.DeviceService;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.SortedSet;
+
+/**
+ * CLI completer for Devices that support Meps.
+ */
+public class CfmDeviceIdCompleter implements Completer {
+    @Override
+    public int complete(String buffer, int cursor, List<String> candidates) {
+        // Delegate string completer
+        StringsCompleter delegate = new StringsCompleter();
+
+        // Fetch our service and feed it's offerings to the string completer
+        DeviceService service = AbstractShellCommand.get(DeviceService.class);
+        Iterator<Device> it = service.getDevices().iterator();
+        SortedSet<String> strings = delegate.getStrings();
+        while (it.hasNext()) {
+            Device device = it.next();
+            if (device.is(CfmMepProgrammable.class)) {
+                strings.add(device.id().toString());
+            }
+        }
+
+        // Now let the completer do the work for figuring out what to offer.
+        return delegate.complete(buffer, cursor, candidates);
+    }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmMepIdCompleter.java b/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmMepIdCompleter.java
new file mode 100644
index 0000000..6019916
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmMepIdCompleter.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2018-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.cfm.cli.completer;
+
+import org.onosproject.cli.AbstractChoicesCompleter;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMdService;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepService;
+import org.slf4j.Logger;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.onosproject.cli.AbstractShellCommand.get;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * CLI completer for Mep Id creation.
+ */
+public class CfmMepIdCompleter extends AbstractChoicesCompleter {
+    private final Logger log = getLogger(getClass());
+
+    @Override
+    public List<String> choices() {
+        List<String> choices = new ArrayList<>();
+
+        CfmMdService mdService = get(CfmMdService.class);
+        CfmMepService mepService = get(CfmMepService.class);
+
+        mdService.getAllMaintenanceDomain().forEach(md -> {
+            choices.add(new StringBuilder(md.mdId().mdName())
+                            .append("(")
+                            .append(md.mdId().nameType())
+                            .append(")").toString());
+
+            md.maintenanceAssociationList().forEach(ma -> {
+                    choices.add(new StringBuilder(md.mdId().mdName())
+                            .append("(")
+                            .append(md.mdId().nameType())
+                            .append(") ")
+                            .append(ma.maId().maName())
+                            .append("(")
+                            .append(ma.maId().nameType())
+                            .append(")").toString());
+
+                    try {
+                        mepService.getAllMeps(md.mdId(), ma.maId()).forEach(mep ->
+                                choices.add(new StringBuilder(md.mdId().mdName())
+                                        .append("(")
+                                        .append(md.mdId().nameType())
+                                        .append(") ")
+                                        .append(ma.maId().maName())
+                                        .append("(")
+                                        .append(ma.maId().nameType())
+                                        .append(") ")
+                                        .append(mep.mepId()).toString())
+                        );
+                    } catch (CfmConfigException e) {
+                        log.warn("Unable to retrieve mep details", e);
+                    }
+                }
+            );
+        });
+
+        return choices;
+    }
+
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/rest/CfmWebApplication.java b/apps/cfm/src/main/java/org/onosproject/cfm/rest/CfmWebApplication.java
index 13157f2..b32174e 100644
--- a/apps/cfm/src/main/java/org/onosproject/cfm/rest/CfmWebApplication.java
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/rest/CfmWebApplication.java
@@ -32,6 +32,7 @@
                 MdWebResource.class,
                 MaWebResource.class,
                 MepWebResource.class,
+                DeviceMepWebResource.class,
                 DmWebResource.class,
                 LmWebResource.class);
     }
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/rest/DeviceMepWebResource.java b/apps/cfm/src/main/java/org/onosproject/cfm/rest/DeviceMepWebResource.java
new file mode 100644
index 0000000..5fbed6d
--- /dev/null
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/rest/DeviceMepWebResource.java
@@ -0,0 +1,69 @@
+/*
+ * 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.cfm.rest;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import org.onosproject.incubator.net.l2monitoring.cfm.Mep;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.rest.AbstractWebResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.util.Collection;
+
+/**
+ * Layer 2 CFM Maintenance Association Endpoint (MEP) by Device web resource.
+ */
+@Path("device")
+public class DeviceMepWebResource extends AbstractWebResource {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    /**
+     * Get all MEPs by Device Id. The device should support Meps
+     *
+     * @param deviceId The id of a device.
+     * @return 200 OK with a list of MEPS or 500 on error
+     */
+    @GET
+    @Path("{device_id}")
+    @Produces(MediaType.APPLICATION_JSON)
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response getAllMepsForDevice(@PathParam("device_id") String deviceId) {
+        DeviceId deviceIdObj = DeviceId.deviceId(deviceId);
+        log.debug("GET all Meps called for Device {}", deviceIdObj);
+        try {
+            Collection<Mep> mepCollection = get(CfmMepService.class)
+                    .getAllMepsByDevice(deviceIdObj);
+            ArrayNode an = mapper().createArrayNode();
+            an.add(codec(Mep.class).encode(mepCollection, this));
+            return ok(mapper().createObjectNode().set("meps", an)).build();
+        } catch (CfmConfigException e) {
+            log.error("Get all Meps on device {} failed because of exception",
+                    deviceIdObj, e);
+            return Response.serverError().entity("{ \"failure\":\"" + e.toString() + "\" }").build();
+        }
+    }
+}
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/rest/MdWebResource.java b/apps/cfm/src/main/java/org/onosproject/cfm/rest/MdWebResource.java
index c642f4a..0e523a7 100644
--- a/apps/cfm/src/main/java/org/onosproject/cfm/rest/MdWebResource.java
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/rest/MdWebResource.java
@@ -82,6 +82,7 @@
         log.debug("GET called for MD {}", mdName);
         try {
             MaintenanceDomain md = get(CfmMdService.class)
+                    //FIXME Handle other types of name constructs e.g. DomainName
                     .getMaintenanceDomain(MdIdCharStr.asMdId(mdName))
                     .orElseThrow(() -> new IllegalArgumentException(
                             "MD " + mdName + " not Found"));
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/rest/MepWebResource.java b/apps/cfm/src/main/java/org/onosproject/cfm/rest/MepWebResource.java
index fa8c728..52d4150 100644
--- a/apps/cfm/src/main/java/org/onosproject/cfm/rest/MepWebResource.java
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/rest/MepWebResource.java
@@ -18,6 +18,7 @@
 import java.io.InputStream;
 import java.net.URI;
 import java.util.Collection;
+import java.util.Optional;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
@@ -145,7 +146,7 @@
             MdId mdId = MdIdCharStr.asMdId(mdName);
             MaIdShort maId = MaIdCharStr.asMaId(maName);
             boolean deleted = get(CfmMepService.class)
-                    .deleteMep(mdId, maId, MepId.valueOf(mepIdShort));
+                    .deleteMep(mdId, maId, MepId.valueOf(mepIdShort), Optional.empty());
             if (!deleted) {
                 return Response.notModified(mdName + "/" + maName + "/" +
                         mepIdShort + " did not exist").build();
@@ -187,8 +188,8 @@
 
             Mep mep = ((MepCodec) mepCodec).decode((ObjectNode) cfg, this, mdName, maName);
 
-            Boolean issuccess = get(CfmMepService.class).createMep(mdId, maId, mep);
-            if (!issuccess) {
+            Boolean didNotExist = get(CfmMepService.class).createMep(mdId, maId, mep);
+            if (!didNotExist) {
                 return Response.notModified(mdName + "/" + ma.maId() + "/" + mep.mepId() +
                         " already exists").build();
             }
diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/web/MaintenanceAssociationCodec.java b/apps/cfm/src/main/java/org/onosproject/cfm/web/MaintenanceAssociationCodec.java
index 62be35d..6c3829f 100644
--- a/apps/cfm/src/main/java/org/onosproject/cfm/web/MaintenanceAssociationCodec.java
+++ b/apps/cfm/src/main/java/org/onosproject/cfm/web/MaintenanceAssociationCodec.java
@@ -124,10 +124,13 @@
                 builder = builder.addToComponentList(component);
             }
 
-            List<MepId> remoteMeps = (new RMepCodec()).decode(
-                    (ArrayNode) nullIsIllegal(maNode.get(RMEP_LIST), "rmep-list is required"), context);
-            for (MepId remoteMep:remoteMeps) {
-                builder = builder.addToRemoteMepIdList(remoteMep);
+            JsonNode rmepListJson = maNode.get(RMEP_LIST);
+            if (rmepListJson != null) {
+                List<MepId> remoteMeps = (new RMepCodec()).decode(
+                        (ArrayNode) rmepListJson, context);
+                for (MepId remoteMep:remoteMeps) {
+                    builder = builder.addToRemoteMepIdList(remoteMep);
+                }
             }
 
             return builder.build();
diff --git a/apps/cfm/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/cfm/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index 5f53eba..fcd66a0 100644
--- a/apps/cfm/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/apps/cfm/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -57,6 +57,18 @@
                 <ref component-id="maNameCompleter"/>
             </completers>
         </command>
+        <command>
+            <action class="org.onosproject.cfm.cli.CfmMepListCommand"/>
+            <completers>
+                <ref component-id="mepIdCompleter"/>
+            </completers>
+        </command>
+        <command>
+            <action class="org.onosproject.cfm.cli.CfmMepListDeviceCommand"/>
+            <completers>
+                <ref component-id="mepDeviceIdCompleter"/>
+            </completers>
+        </command>
     </command-bundle>
 
     <bean id="placeholderCompleter" class="org.onosproject.cli.PlaceholderCompleter"/>
@@ -68,6 +80,8 @@
     <bean id="maCcmIntervalCompleter" class="org.onosproject.cfm.cli.completer.CfmMaCcmIntervalCompleter"/>
     <bean id="compTagTypeCompleter" class="org.onosproject.cfm.cli.completer.CfmCompTagTypeCompleter"/>
     <bean id="compMhfCompleter" class="org.onosproject.cfm.cli.completer.CfmCompMhfCompleter"/>
+    <bean id="mepIdCompleter" class="org.onosproject.cfm.cli.completer.CfmMepIdCompleter"/>
+    <bean id="mepDeviceIdCompleter" class="org.onosproject.cfm.cli.completer.CfmDeviceIdCompleter"/>
 
 </blueprint>
 
diff --git a/apps/cfm/src/main/resources/definitions/MaCreate.json b/apps/cfm/src/main/resources/definitions/MaCreate.json
index fea36db..5b6be27 100644
--- a/apps/cfm/src/main/resources/definitions/MaCreate.json
+++ b/apps/cfm/src/main/resources/definitions/MaCreate.json
@@ -133,7 +133,8 @@
               }
             }
           },
-          "example": [{"rmep": 10}, {"rmep": 20}]
+          "example": [{"rmep": 10}, {"rmep": 20}],
+          "description": "An optional set of Mep IDs that might be on equipment not managed by ONOS"
         }
       }
     }
diff --git a/apps/cfm/src/test/java/org/onosproject/cfm/impl/MaWebResourceTest.java b/apps/cfm/src/test/java/org/onosproject/cfm/impl/MaWebResourceTest.java
index 6378ea9..8216dde 100644
--- a/apps/cfm/src/test/java/org/onosproject/cfm/impl/MaWebResourceTest.java
+++ b/apps/cfm/src/test/java/org/onosproject/cfm/impl/MaWebResourceTest.java
@@ -22,7 +22,6 @@
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
 import org.onlab.packet.VlanId;
-import org.onlab.rest.BaseResource;
 import org.onosproject.cfm.CfmCodecContext;
 import org.onosproject.codec.CodecService;
 import org.onosproject.incubator.net.l2monitoring.cfm.Component;
@@ -43,7 +42,6 @@
 import javax.ws.rs.client.Entity;
 import javax.ws.rs.client.WebTarget;
 import javax.ws.rs.core.Response;
-
 import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
@@ -72,7 +70,7 @@
         ServiceDirectory testDirectory = new TestServiceDirectory()
                 .add(CfmMdService.class, mdService)
                 .add(CodecService.class, context.codecManager());
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
 
         ma1 = DefaultMaintenanceAssociation
                 .builder(MANAME1, MDNAME1.getNameLength())
diff --git a/apps/cfm/src/test/java/org/onosproject/cfm/impl/MdWebResourceTest.java b/apps/cfm/src/test/java/org/onosproject/cfm/impl/MdWebResourceTest.java
index f4c9b5a..3c65176 100644
--- a/apps/cfm/src/test/java/org/onosproject/cfm/impl/MdWebResourceTest.java
+++ b/apps/cfm/src/test/java/org/onosproject/cfm/impl/MdWebResourceTest.java
@@ -21,7 +21,6 @@
 import org.junit.Test;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
-import org.onlab.rest.BaseResource;
 import org.onosproject.cfm.CfmCodecContext;
 import org.onosproject.codec.CodecService;
 import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMaintenanceDomain;
@@ -65,7 +64,7 @@
         ServiceDirectory testDirectory = new TestServiceDirectory()
                 .add(CfmMdService.class, mdService)
                 .add(CodecService.class, context.codecManager());
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
 
         mdList = new ArrayList<>();
 
diff --git a/apps/cfm/src/test/java/org/onosproject/cfm/impl/MepWebResourceTest.java b/apps/cfm/src/test/java/org/onosproject/cfm/impl/MepWebResourceTest.java
index 4dd83db..6a5dbca 100644
--- a/apps/cfm/src/test/java/org/onosproject/cfm/impl/MepWebResourceTest.java
+++ b/apps/cfm/src/test/java/org/onosproject/cfm/impl/MepWebResourceTest.java
@@ -22,7 +22,6 @@
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
 import org.onlab.packet.VlanId;
-import org.onlab.rest.BaseResource;
 import org.onosproject.cfm.CfmCodecContext;
 import org.onosproject.codec.CodecService;
 import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMaintenanceAssociation;
@@ -86,7 +85,7 @@
                 .add(CfmMepService.class, mepService)
                 .add(CfmMdService.class, mdService)
                 .add(CodecService.class, context.codecManager());
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
 
         mepEntry1 = DefaultMepEntry.builder(
                     MEPID1,
@@ -186,7 +185,7 @@
     @Test
     public void testDeleteMepValid() throws CfmConfigException {
 
-        expect(mepService.deleteMep(MDNAME1, MANAME1, MepId.valueOf((short) 1)))
+        expect(mepService.deleteMep(MDNAME1, MANAME1, MepId.valueOf((short) 1), Optional.empty()))
                 .andReturn(true).anyTimes();
         replay(mepService);
 
@@ -200,7 +199,7 @@
     @Test
     public void testDeleteMepNotFound() throws CfmConfigException {
 
-        expect(mepService.deleteMep(MDNAME1, MANAME1, MepId.valueOf((short) 2)))
+        expect(mepService.deleteMep(MDNAME1, MANAME1, MepId.valueOf((short) 2), Optional.empty()))
                 .andReturn(false).anyTimes();
         replay(mepService);
 
diff --git a/apps/cfm/src/test/java/org/onosproject/soam/impl/DmWebResourceTest.java b/apps/cfm/src/test/java/org/onosproject/soam/impl/DmWebResourceTest.java
index 702bc09..6fac3a3 100644
--- a/apps/cfm/src/test/java/org/onosproject/soam/impl/DmWebResourceTest.java
+++ b/apps/cfm/src/test/java/org/onosproject/soam/impl/DmWebResourceTest.java
@@ -21,7 +21,6 @@
 import org.junit.Test;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
-import org.onlab.rest.BaseResource;
 import org.onosproject.cfm.CfmCodecContext;
 import org.onosproject.cfm.impl.CfmResourceTest;
 import org.onosproject.codec.CodecService;
@@ -50,7 +49,6 @@
 import javax.ws.rs.client.Entity;
 import javax.ws.rs.client.WebTarget;
 import javax.ws.rs.core.Response;
-
 import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
@@ -61,7 +59,9 @@
 import java.util.List;
 
 import static junit.framework.TestCase.fail;
-import static org.easymock.EasyMock.*;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
 import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
@@ -88,7 +88,7 @@
                 .add(CfmMepService.class, mepService)
                 .add(SoamService.class, soamService)
                 .add(CodecService.class, context.codecManager());
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
 
         DelayMeasurementStatCurrent.DmStatCurrentBuilder dmCurrBuilder1 =
                 (DelayMeasurementStatCurrent.DmStatCurrentBuilder)
diff --git a/apps/cfm/src/test/java/org/onosproject/soam/impl/LmWebResourceTest.java b/apps/cfm/src/test/java/org/onosproject/soam/impl/LmWebResourceTest.java
index 9dd70e6..44b4960 100644
--- a/apps/cfm/src/test/java/org/onosproject/soam/impl/LmWebResourceTest.java
+++ b/apps/cfm/src/test/java/org/onosproject/soam/impl/LmWebResourceTest.java
@@ -21,7 +21,6 @@
 import org.junit.Test;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
-import org.onlab.rest.BaseResource;
 import org.onosproject.cfm.CfmCodecContext;
 import org.onosproject.cfm.impl.CfmResourceTest;
 import org.onosproject.codec.CodecService;
@@ -87,7 +86,7 @@
                 .add(CfmMepService.class, mepService)
                 .add(SoamService.class, soamService)
                 .add(CodecService.class, context.codecManager());
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
 
         lm1 = DefaultLmEntry.builder(
                     DelayMeasurementCreate.Version.Y17312008,
diff --git a/apps/config/src/main/java/org/onosproject/config/impl/DistributedDynamicConfigStore.java b/apps/config/src/main/java/org/onosproject/config/impl/DistributedDynamicConfigStore.java
index 3923173..8f1662c 100644
--- a/apps/config/src/main/java/org/onosproject/config/impl/DistributedDynamicConfigStore.java
+++ b/apps/config/src/main/java/org/onosproject/config/impl/DistributedDynamicConfigStore.java
@@ -300,7 +300,7 @@
         entries = complete(ret);
         log.trace(" keystore.getChildren({})", spath);
         log.trace("  entries keys:{}", entries.keySet());
-        if ((entries != null) && (!entries.isEmpty())) {
+        if (!entries.isEmpty()) {
             entries.forEach((k, v) -> {
                 String[] names = k.split(ResourceIdParser.NM_CHK);
                 String name = names[0];
diff --git a/apps/configsync-netconf/pom.xml b/apps/configsync-netconf/pom.xml
index 4f832bc..7a23711 100644
--- a/apps/configsync-netconf/pom.xml
+++ b/apps/configsync-netconf/pom.xml
@@ -124,18 +124,6 @@
     </dependencies>
 
     <build>
-        <pluginManagement>
-            <plugins>
-
-                <plugin>
-                    <groupId>org.apache.karaf.tooling</groupId>
-                    <artifactId>karaf-maven-plugin</artifactId>
-                    <version>3.0.5</version>
-                    <extensions>true</extensions>
-                </plugin>
-
-            </plugins>
-        </pluginManagement>
 
         <plugins>
             <plugin>
diff --git a/apps/configsync/pom.xml b/apps/configsync/pom.xml
index 270dda3..1376461 100644
--- a/apps/configsync/pom.xml
+++ b/apps/configsync/pom.xml
@@ -124,18 +124,6 @@
     </dependencies>
 
     <build>
-        <pluginManagement>
-            <plugins>
-
-                <plugin>
-                    <groupId>org.apache.karaf.tooling</groupId>
-                    <artifactId>karaf-maven-plugin</artifactId>
-                    <version>3.0.5</version>
-                    <extensions>true</extensions>
-                </plugin>
-
-            </plugins>
-        </pluginManagement>
 
         <plugins>
             <plugin>
diff --git a/apps/cpman/api/src/main/java/org/onosproject/cpman/ControlLoadSnapshot.java b/apps/cpman/api/src/main/java/org/onosproject/cpman/ControlLoadSnapshot.java
index b3f34ea..532297d 100644
--- a/apps/cpman/api/src/main/java/org/onosproject/cpman/ControlLoadSnapshot.java
+++ b/apps/cpman/api/src/main/java/org/onosproject/cpman/ControlLoadSnapshot.java
@@ -99,7 +99,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(latest, average, time, recent);
+        return Objects.hash(latest, average, time, Arrays.hashCode(recent));
     }
 
     @Override
diff --git a/apps/cpman/app/src/main/java/org/onosproject/cpman/rest/SystemMetricsCollectorWebResource.java b/apps/cpman/app/src/main/java/org/onosproject/cpman/rest/SystemMetricsCollectorWebResource.java
index ca10f86..dc7ea22 100644
--- a/apps/cpman/app/src/main/java/org/onosproject/cpman/rest/SystemMetricsCollectorWebResource.java
+++ b/apps/cpman/app/src/main/java/org/onosproject/cpman/rest/SystemMetricsCollectorWebResource.java
@@ -93,7 +93,7 @@
             ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
 
             if (jsonTree == null || !checkFields(jsonTree, CPU_FIELD_SET)) {
-                ok(root).build();
+                return ok(root).build();
             }
 
             long cpuLoad = nullIsIllegal((long) (jsonTree.get("cpuLoad").asDouble()
@@ -154,7 +154,7 @@
             ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
 
             if (jsonTree == null || !checkFields(jsonTree, MEMORY_FIELD_SET)) {
-                ok(root).build();
+                return ok(root).build();
             }
 
             long memUsed = nullIsIllegal(jsonTree.get("memoryUsed").asLong(), INVALID_REQUEST);
diff --git a/apps/cpman/app/src/test/java/org/onosproject/cpman/rest/ControlMetricsCollectorResourceTest.java b/apps/cpman/app/src/test/java/org/onosproject/cpman/rest/ControlMetricsCollectorResourceTest.java
index b505cd5..a225113 100644
--- a/apps/cpman/app/src/test/java/org/onosproject/cpman/rest/ControlMetricsCollectorResourceTest.java
+++ b/apps/cpman/app/src/test/java/org/onosproject/cpman/rest/ControlMetricsCollectorResourceTest.java
@@ -32,7 +32,6 @@
 import org.onlab.metrics.MetricsService;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
-import org.onlab.rest.BaseResource;
 import org.onosproject.cpman.ControlPlaneMonitorService;
 import org.onosproject.cpman.SystemInfo;
 import org.onosproject.cpman.impl.SystemInfoFactory;
@@ -86,7 +85,7 @@
                 new TestServiceDirectory()
                         .add(ControlPlaneMonitorService.class, mockControlPlaneMonitorService)
                         .add(MetricsService.class, mockMetricsService);
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
     }
 
     /**
diff --git a/apps/cpman/app/src/test/java/org/onosproject/cpman/rest/ControlMetricsResourceTest.java b/apps/cpman/app/src/test/java/org/onosproject/cpman/rest/ControlMetricsResourceTest.java
index 8549167..7c48680 100644
--- a/apps/cpman/app/src/test/java/org/onosproject/cpman/rest/ControlMetricsResourceTest.java
+++ b/apps/cpman/app/src/test/java/org/onosproject/cpman/rest/ControlMetricsResourceTest.java
@@ -22,7 +22,6 @@
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
 import org.onlab.packet.IpAddress;
-import org.onlab.rest.BaseResource;
 import org.onosproject.cluster.ClusterService;
 import org.onosproject.cluster.ControllerNode;
 import org.onosproject.cluster.NodeId;
@@ -151,7 +150,7 @@
                                 mockControlPlaneMonitorService)
                         .add(ClusterService.class, mockClusterService)
                         .add(CodecService.class, codecService);
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
 
         nodeId = new NodeId("1");
         mockControlLoad = new MockControlLoad();
diff --git a/apps/dhcp/api/src/main/java/org/onosproject/dhcp/IpAssignment.java b/apps/dhcp/api/src/main/java/org/onosproject/dhcp/IpAssignment.java
index d4beda2..7b83f0c 100644
--- a/apps/dhcp/api/src/main/java/org/onosproject/dhcp/IpAssignment.java
+++ b/apps/dhcp/api/src/main/java/org/onosproject/dhcp/IpAssignment.java
@@ -310,7 +310,6 @@
         private void validateInputs() {
             checkNotNull(ipAddress, "IP Address must be specified");
             checkNotNull(assignmentStatus, "Assignment Status must be specified");
-            checkNotNull(leasePeriod, "Lease Period must be specified");
             checkNotNull(timeStamp, "Timestamp must be specified");
 
             switch (assignmentStatus) {
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerImpl.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerImpl.java
index b45433f..5b255ab 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerImpl.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerImpl.java
@@ -792,9 +792,6 @@
             }
         }
 
-        // Remove broadcast flag
-        dhcpPacket.setFlags((short) 0);
-
         udpPacket.setPayload(dhcpPacket);
         // As a DHCP relay, the source port should be server port( instead
         // of client port.
@@ -1017,14 +1014,18 @@
             // if client is indirectly connected, try use next hop mac address
             MacAddress macAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
             HostId hostId = HostId.hostId(macAddress, vlanId);
-            DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
-            if (record != null) {
-                // if next hop can be found, use mac address of next hop
-                record.nextHop().ifPresent(etherReply::setDestinationMACAddress);
+            if (((int) dhcpPayload.getFlags() & 0x8000) == 0x0000) {
+                DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
+                if (record != null) {
+                    // if next hop can be found, use mac address of next hop
+                    record.nextHop().ifPresent(etherReply::setDestinationMACAddress);
+                } else {
+                    // otherwise, discard the packet
+                    log.warn("Can't find record for host id {}, discard packet", hostId);
+                    return null;
+                }
             } else {
-                // otherwise, discard the packet
-                log.warn("Can't find record for host id {}, discard packet", hostId);
-                return null;
+                etherReply.setDestinationMACAddress(MacAddress.BROADCAST);
             }
         } else {
             etherReply.setDestinationMACAddress(dhcpPayload.getClientHardwareAddress());
@@ -1043,7 +1044,11 @@
         // SRC_IP: relay agent IP
         // DST_IP: offered IP
         ipv4Packet.setSourceAddress(ipFacingClient.toInt());
-        ipv4Packet.setDestinationAddress(dhcpPayload.getYourIPAddress());
+        if (((int) dhcpPayload.getFlags() & 0x8000) == 0x0000) {
+            ipv4Packet.setDestinationAddress(dhcpPayload.getYourIPAddress());
+        } else {
+            ipv4Packet.setDestinationAddress(BROADCAST_IP);
+        }
         udpPacket.setSourcePort(UDP.DHCP_SERVER_PORT);
         if (directlyConnected(dhcpPayload)) {
             udpPacket.setDestinationPort(UDP.DHCP_CLIENT_PORT);
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java
index 9a8b3e0..0cb3468 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java
@@ -14,7 +14,6 @@
  * limitations under the License.
  *
  */
-
 package org.onosproject.dhcprelay;
 
 import com.google.common.collect.HashMultimap;
@@ -42,12 +41,14 @@
 import org.onlab.packet.VlanId;
 import org.onlab.packet.dhcp.Dhcp6RelayOption;
 import org.onlab.packet.dhcp.Dhcp6InterfaceIdOption;
-import org.onlab.packet.dhcp.Dhcp6Option;
 import org.onlab.packet.dhcp.Dhcp6IaNaOption;
 import org.onlab.packet.dhcp.Dhcp6IaTaOption;
 import org.onlab.packet.dhcp.Dhcp6IaPdOption;
 import org.onlab.packet.dhcp.Dhcp6IaAddressOption;
 import org.onlab.packet.dhcp.Dhcp6IaPrefixOption;
+import org.onlab.packet.dhcp.Dhcp6ClientIdOption;
+import org.onlab.packet.dhcp.Dhcp6Duid;
+import org.onlab.packet.DHCP6.MsgType;
 import org.onlab.util.HexString;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
@@ -55,7 +56,10 @@
 import org.onosproject.dhcprelay.api.DhcpServerInfo;
 import org.onosproject.dhcprelay.config.IgnoreDhcpConfig;
 import org.onosproject.dhcprelay.store.DhcpRelayStore;
+import org.onosproject.dhcprelay.store.DhcpRecord;
 import org.onosproject.dhcprelay.store.DhcpFpmPrefixStore;
+import org.onosproject.dhcprelay.store.DhcpRelayCounters;
+import org.onosproject.dhcprelay.store.DhcpRelayCountersStore;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
 import org.onosproject.routing.fpm.api.FpmRecord;
@@ -97,6 +101,7 @@
 import org.slf4j.LoggerFactory;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.dhcprelay.Dhcp6HandlerUtil.InternalPacket;
 
 
 import java.nio.ByteBuffer;
@@ -106,12 +111,11 @@
 import java.util.Set;
 import java.util.ArrayList;
 import java.util.concurrent.atomic.AtomicInteger;
-
-
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
 import static org.onosproject.net.flowobjective.Objective.Operation.ADD;
 import static org.onosproject.net.flowobjective.Objective.Operation.REMOVE;
+import java.util.concurrent.Semaphore;
 
 @Component
 @Service
@@ -120,7 +124,7 @@
     public static final String DHCP_V6_RELAY_APP = "org.onosproject.Dhcp6HandlerImpl";
     public static final ProviderId PROVIDER_ID = new ProviderId("dhcp6", DHCP_V6_RELAY_APP);
     private static final int IGNORE_CONTROL_PRIORITY = PacketPriority.CONTROL.priorityValue() + 1000;
-
+    private String gCount = "global";
     private static final TrafficSelector CLIENT_SERVER_SELECTOR = DefaultTrafficSelector.builder()
             .matchEthType(Ethernet.TYPE_IPV6)
             .matchIPProtocol(IPv6.PROTOCOL_UDP)
@@ -145,6 +149,9 @@
     protected DhcpRelayStore dhcpRelayStore;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DhcpRelayCountersStore dhcpRelayCountersStore;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected PacketService packetService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -178,9 +185,22 @@
 
     private Boolean dhcpFpmEnabled = false;
 
+    private Dhcp6HandlerUtil dhcp6HandlerUtil = new Dhcp6HandlerUtil();
+
     private List<DhcpServerInfo> defaultServerInfoList = Lists.newArrayList();
     private List<DhcpServerInfo> indirectServerInfoList = Lists.newArrayList();
+    private class IpAddressInfo {
+        Ip6Address ip6Address;
+        long    prefTime;
+    }
+    private class PdPrefixInfo {
+        IpPrefix pdPrefix;
+        long    prefTime;
+    }
+    protected int dhcp6PollInterval = 24 * 3600; // 24 hr period
 
+    // max 1 thread
+    static Semaphore recordSemaphore = new Semaphore(1);
 
     // CLIENT message types
     public static final Set<Byte> MSG_TYPE_FROM_CLIENT =
@@ -275,16 +295,18 @@
         Ethernet receivedPacket = context.inPacket().parsed();
 
         if (!configured()) {
-            log.warn("Missing DHCP6 relay server config. Abort packet processing");
-            log.trace("dhcp6 payload {}", dhcp6Payload);
-
+            log.warn("Missing DHCP6 relay server config. Abort packet processing dhcp6 payload {}", dhcp6Payload);
             return;
         }
-
-        byte msgType = dhcp6Payload.getMsgType();
+        byte msgTypeVal = dhcp6Payload.getMsgType();
+        MsgType msgType = DHCP6.MsgType.getType(msgTypeVal);
+        log.debug("msgType is {}", msgType);
 
         ConnectPoint inPort = context.inPacket().receivedFrom();
 
+        if (inPort == null) {
+            log.warn("incomming ConnectPoint is null");
+        }
         Set<Interface> receivingInterfaces = interfaceService.getInterfacesByPort(inPort);
         //ignore the packets if dhcp client interface is not configured on onos.
         if (receivingInterfaces.isEmpty()) {
@@ -292,27 +314,25 @@
             return;
         }
 
+        if (MSG_TYPE_FROM_CLIENT.contains(msgTypeVal)) {
 
-        if (MSG_TYPE_FROM_CLIENT.contains(msgType)) {
-            InternalPacket ethernetClientPacket =
+            List<InternalPacket> ethernetClientPacket =
                     processDhcp6PacketFromClient(context, receivedPacket, receivingInterfaces);
-            if (ethernetClientPacket != null) {
-                forwardPacket(ethernetClientPacket);
+            for (InternalPacket internalPacket : ethernetClientPacket) {
+                forwardPacket(internalPacket);
             }
-
-        } else if (MSG_TYPE_FROM_SERVER.contains(msgType)) {
-            log.debug("calling processDhcp6PacketFromServer with RELAY_REPL", msgType);
+        } else if (MSG_TYPE_FROM_SERVER.contains(msgTypeVal)) {
+            log.debug("calling processDhcp6PacketFromServer with RELAY_REPL {}", msgTypeVal);
             InternalPacket ethernetPacketReply =
                     processDhcp6PacketFromServer(context, receivedPacket, receivingInterfaces);
             if (ethernetPacketReply != null) {
                 forwardPacket(ethernetPacketReply);
             }
         } else {
-            log.warn("DHCP type {} not supported yet", msgType);
+            log.warn("Not so fast, packet type {} not supported yet", msgTypeVal);
         }
     }
 
-
     /**
      * Checks if this app has been configured.
      *
@@ -332,19 +352,7 @@
         // Do nothing here
     }
 
-    // the new class the contains Ethernet packet and destination port, kind of like adding
-    // internal header to the packet
-    private class InternalPacket {
-        Ethernet packet;
-        ConnectPoint destLocation;
-        public InternalPacket(Ethernet newPacket, ConnectPoint newLocation) {
-            packet = newPacket;
-            destLocation = newLocation;
-        }
-        void setLocation(ConnectPoint newLocation) {
-            destLocation = newLocation;
-        }
-    }
+
 
     //forward the packet to ConnectPoint where the DHCP server is attached.
     private void forwardPacket(InternalPacket packet) {
@@ -361,168 +369,17 @@
         } // if
     }
 
-    /**
-     * Check if the host is directly connected to the network or not.
-     *
-     * @param dhcp6Payload the dhcp6 payload
-     * @return true if the host is directly connected to the network; false otherwise
-     */
-    private boolean directlyConnected(DHCP6 dhcp6Payload) {
-        log.debug("directlyConnected enters");
-
-        if (dhcp6Payload.getMsgType() != DHCP6.MsgType.RELAY_FORW.value() &&
-                dhcp6Payload.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
-            log.debug("directlyConnected true. MsgType {}", dhcp6Payload.getMsgType());
-
-            return true;
-        }
-
-        // Regardless of relay-forward or relay-replay, check if we see another relay message
-        DHCP6 dhcp6Payload2 = dhcp6PacketFromRelayPacket(dhcp6Payload);
-        if (dhcp6Payload2 != null) {
-            if (dhcp6Payload.getMsgType() == DHCP6.MsgType.RELAY_FORW.value()) {
-                log.debug("directlyConnected  false. 1st realy-foward, 2nd MsgType {}", dhcp6Payload2.getMsgType());
-                return false;
-            } else {
-                // relay-reply
-                if (dhcp6Payload2.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
-                    log.debug("directlyConnected  true. 2nd MsgType {}", dhcp6Payload2.getMsgType());
-                    return true;  // must be directly connected
-                } else {
-                    log.debug("directlyConnected  false. 1st relay-reply, 2nd relay-reply MsgType {}",
-                               dhcp6Payload2.getMsgType());
-                    return false;  // must be indirectly connected
-                }
-            }
-        } else {
-            log.debug("directlyConnected  true.");
-            return true;
-        }
-    }
-
-    /**
-     * extract DHCP6 payload from dhcp6 relay message within relay-forwrd/reply.
-     *
-     * @param dhcp6 dhcp6 relay-reply or relay-foward
-     * @return dhcp6Packet dhcp6 packet extracted from relay-message
-     */
-    private DHCP6 dhcp6PacketFromRelayPacket(DHCP6 dhcp6) {
-        log.debug("dhcp6PacketFromRelayPacket  enters. dhcp6 {}", dhcp6);
-
-        // extract the relay message if exist
-        DHCP6 dhcp6Payload = dhcp6.getOptions().stream()
-                    .filter(opt -> opt instanceof Dhcp6RelayOption)
-                    .map(BasePacket::getPayload)
-                    .map(pld -> (DHCP6) pld)
-                    .findFirst()
-                    .orElse(null);
 
 
-        if (dhcp6Payload == null) {
-            // Can't find dhcp payload
-            log.debug("Can't find dhcp6 payload from relay message");
-        } else {
-            log.debug("dhcp6 payload found from relay message {}", dhcp6Payload);
-        }
-
-        return dhcp6Payload;
-    }
-
-    /**
-     * find the leaf DHCP6 packet from multi-level relay packet.
-     *
-     * @param relayPacket dhcp6 relay packet
-     * @return leafPacket non-relay dhcp6 packet
-     */
-    private DHCP6 getDhcp6Leaf(DHCP6 relayPacket) {
-        DHCP6 dhcp6Parent = relayPacket;
-        DHCP6 dhcp6Child = null;
-
-        log.debug("getDhcp6Leaf entered.");
-        while (dhcp6Parent != null) {
-            dhcp6Child = dhcp6PacketFromRelayPacket(dhcp6Parent);
-
-            if (dhcp6Child != null) {
-                if (dhcp6Child.getMsgType() != DHCP6.MsgType.RELAY_FORW.value() &&
-                        dhcp6Child.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
-                    log.debug("leaf dhcp6 packet found.");
-                    break;
-                } else {
-                    // found another relay
-                    // go for another loop
-                    dhcp6Parent = dhcp6Child;
-                }
-            } else {
-                log.warn("Expected dhcp6 within relay pkt, but no dhcp6 leaf found.");
-                break;
-            }
-        }
-        return dhcp6Child;
-    }
-
-    /**
-     * check if DHCP6 relay-reply is reply.
-     *
-     * @param relayPacket dhcp6 relay-reply
-     * @return boolean relay-reply contains ack
-     */
-    private boolean isDhcp6Reply(DHCP6 relayPacket) {
-        log.debug("isDhcp6Reply  entered.");
-
-        DHCP6 leafDhcp6 = getDhcp6Leaf(relayPacket);
-
-        if (leafDhcp6 != null) {
-            if (leafDhcp6.getMsgType() == DHCP6.MsgType.REPLY.value()) {
-                log.debug("isDhcp6Reply  true.");
-                return true;  // must be directly connected
-            } else {
-                log.debug("isDhcp6Reply false. leaf dhcp6 is not replay. MsgType {}", leafDhcp6.getMsgType());
-            }
-        } else {
-            log.debug("isDhcp6Reply false. Expected dhcp6 within relay pkt but not found.");
-        }
-        log.debug("isDhcp6Reply  false.");
-        return false;
-    }
-
-    /**
-     * check if DHCP6 is release or relay-forward contains release.
-     *
-     * @param dhcp6Payload dhcp6 packet
-     * @return boolean dhcp6 contains release
-     */
-    private boolean isDhcp6Release(DHCP6 dhcp6Payload) {
-
-        log.debug("isDhcp6Release  entered.");
-
-        if (dhcp6Payload.getMsgType() ==  DHCP6.MsgType.RELEASE.value()) {
-            log.debug("isDhcp6Release  true.");
-            return true;  // must be directly connected
-        } else {
-            DHCP6 dhcp6Leaf = getDhcp6Leaf(dhcp6Payload);
-            if (dhcp6Leaf != null) {
-                if (dhcp6Leaf.getMsgType() ==  DHCP6.MsgType.RELEASE.value()) {
-                    log.debug("isDhcp6Release  true. indirectlry connected");
-                    return true;
-                } else {
-                    log.debug("leaf dhcp6 is not release. MsgType {}",  dhcp6Leaf.getMsgType());
-                    return false;
-                }
-            } else {
-                log.debug("isDhcp6Release  false. dhcp6 is niether relay nor release.");
-                return false;
-            }
-        }
-    }
 
     /**
      * extract from dhcp6 packet client ipv6 address of given by dhcp server.
      *
      * @param dhcp6 the dhcp6 packet
-     * @return Ip6Address  Ip6Address given by dhcp server, or null if not exists
+     * @return IpAddressInfo  IpAddressInfo given by dhcp server, or null if not exists
      */
-    private Ip6Address extractIpAddress(DHCP6 dhcp6) {
-        Ip6Address ip = null;
+    private IpAddressInfo extractIpAddress(DHCP6 dhcp6) {
+        IpAddressInfo ipInfo = new IpAddressInfo();
 
         log.debug("extractIpAddress  enters dhcp6 {}.", dhcp6);
         // Extract IPv6 address from IA NA ot IA TA option
@@ -552,18 +409,18 @@
                     .map(opt -> (Dhcp6IaAddressOption) opt)
                     .findFirst();
         } else {
+            log.info("No IPv6 address found from iaTaOption {}", iaTaOption);
             iaAddressOption = Optional.empty();
         }
         if (iaAddressOption.isPresent()) {
-            ip = iaAddressOption.get().getIp6Address();
+            ipInfo.ip6Address = iaAddressOption.get().getIp6Address();
+            ipInfo.prefTime = iaAddressOption.get().getPreferredLifetime();
             log.debug("Found IPv6 address from iaAddressOption {}", iaAddressOption);
-
-
         } else {
             log.debug("Can't find IPv6 address from DHCPv6 {}", dhcp6);
+            return null;
         }
-
-        return ip;
+        return ipInfo;
     }
     /**
      * extract from dhcp6 packet Prefix prefix provided by dhcp server.
@@ -571,11 +428,11 @@
      * @param dhcp6 the dhcp6 payload
      * @return IpPrefix Prefix Delegation prefix, or null if not exists.
      */
-    private IpPrefix extractPrefix(DHCP6 dhcp6) {
-        log.trace("extractPrefix  enters {}", dhcp6);
+    private PdPrefixInfo extractPrefix(DHCP6 dhcp6) {
+        log.debug("extractPrefix  enters {}", dhcp6);
 
         // extract prefix
-        IpPrefix  prefixPrefix = null;
+        PdPrefixInfo  pdPrefixInfo = new PdPrefixInfo();
 
         Ip6Address prefixAddress = null;
 
@@ -588,485 +445,507 @@
 
         Optional<Dhcp6IaPrefixOption> iaPrefixOption;
         if (iaPdOption.isPresent()) {
-            log.trace("IA_PD option found {}", iaPdOption);
+            log.debug("IA_PD option found {}", iaPdOption);
 
             iaPrefixOption = iaPdOption.get().getOptions().stream()
                     .filter(opt -> opt instanceof Dhcp6IaPrefixOption)
                     .map(opt -> (Dhcp6IaPrefixOption) opt)
                     .findFirst();
         } else {
-            log.trace("IA_PD option NOT found");
+            log.debug("IA_PD option NOT found");
 
             iaPrefixOption = Optional.empty();
         }
         if (iaPrefixOption.isPresent()) {
-            log.trace("IAPrefix Option within IA_PD option found {}", iaPrefixOption);
+            log.debug("IAPrefix Option within IA_PD option found {}", iaPrefixOption);
 
             prefixAddress = iaPrefixOption.get().getIp6Prefix();
             int prefixLen = (int) iaPrefixOption.get().getPrefixLength();
-            log.trace("Prefix length is  {} bits", prefixLen);
-            prefixPrefix = IpPrefix.valueOf(prefixAddress, prefixLen);
+            log.debug("Prefix length is  {} bits", prefixLen);
+            pdPrefixInfo.pdPrefix = IpPrefix.valueOf(prefixAddress, prefixLen);
+            pdPrefixInfo.prefTime = iaPrefixOption.get().getPreferredLifetime();
         } else {
-            log.trace("Can't find IPv6 prefix from DHCPv6 {}", dhcp6);
+            log.debug("Can't find IPv6 prefix from DHCPv6 {}", dhcp6);
+            return null;
         }
-
-        return prefixPrefix;
+        return pdPrefixInfo;
     }
 
     /**
-     * remove host or route.
+     * extract from dhcp6 packet ClientIdOption.
+     *
+     * @param directConnFlag directly connected host
+     * @param dhcp6Payload the dhcp6 payload
+     * @return Dhcp6ClientIdOption clientIdOption, or null if not exists.
+     */
+    private Dhcp6ClientIdOption extractClinedId(Boolean directConnFlag, DHCP6 dhcp6Payload) {
+        Dhcp6ClientIdOption clientIdOption;
+
+        if (directConnFlag) {
+            clientIdOption = dhcp6Payload.getOptions()
+                    .stream()
+                    .filter(opt -> opt instanceof Dhcp6ClientIdOption)
+                    .map(opt -> (Dhcp6ClientIdOption) opt)
+                    .findFirst()
+                    .orElse(null);
+        } else {
+            DHCP6 leafDhcp = dhcp6HandlerUtil.getDhcp6Leaf(dhcp6Payload);
+            clientIdOption = leafDhcp.getOptions()
+                    .stream()
+                    .filter(opt -> opt instanceof Dhcp6ClientIdOption)
+                    .map(opt -> (Dhcp6ClientIdOption) opt)
+                    .findFirst()
+                    .orElse(null);
+        }
+
+        return clientIdOption;
+    }
+    /**
+     * remove host or route and update dhcp relay record attributes.
      *
      * @param directConnFlag  flag to show that packet is from directly connected client
+     * @param location  client side connect point
      * @param dhcp6Packet the dhcp6 payload
      * @param clientPacket client's ethernet packet
      * @param clientIpv6 client's Ipv6 packet
      * @param clientInterface client interfaces
      */
-    private void removeHostOrRoute(boolean directConnFlag, DHCP6 dhcp6Packet,
+    private void removeHostOrRoute(boolean directConnFlag, ConnectPoint location,
+                                   DHCP6 dhcp6Packet,
                                    Ethernet clientPacket, IPv6 clientIpv6,
                                    Interface clientInterface) {
-        log.debug("extractPrefix  enters {}", dhcp6Packet);
+        log.debug("removeHostOrRoute  enters {}", dhcp6Packet);
         VlanId vlanId = clientInterface.vlan();
-        MacAddress clientMac = clientPacket.getSourceMAC();
-        log.debug("client mac {} client vlan {}", HexString.toHexString(clientMac.toBytes(), ":"), vlanId);
+        MacAddress srcMac = clientPacket.getSourceMAC();  // could be gw or host
+        MacAddress leafClientMac;
+        byte leafMsgType;
+        log.debug("client mac {} client vlan {}", HexString.toHexString(srcMac.toBytes(), ":"), vlanId);
 
-        // add host or route
-        if (isDhcp6Release(dhcp6Packet)) {
-            IpAddress ip = null;
-            if (directConnFlag) {
-                // Add to host store if it is connected to network directly
-                ip = extractIpAddress(dhcp6Packet);
-                if (ip != null) {
+        Dhcp6ClientIdOption clientIdOption = extractClinedId(directConnFlag, dhcp6Packet);
+        if (clientIdOption != null) {
+            if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
+                    (clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
+                leafClientMac = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
+            } else {
+                log.warn("Link-Layer Address not supported in CLIENTID option. No DhcpRelay Record created.");
+                dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_FAIL);
+                return;
+            }
+        } else {
+            log.warn("CLIENTID option NOT found. Don't create DhcpRelay Record.");
+            dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENTID_FAIL);
+            return;
+        }
 
-                    HostId hostId = HostId.hostId(clientMac, vlanId);
+        HostId leafHostId = HostId.hostId(leafClientMac, vlanId);
+        DhcpRecord record = dhcpRelayStore.getDhcpRecord(leafHostId).orElse(null);
+        if (record == null) {
+            record = new DhcpRecord(leafHostId);
+        }  else {
+            record = record.clone();
+        }
+
+        Boolean isMsgRelease = dhcp6HandlerUtil.isDhcp6Release(dhcp6Packet);
+        IpAddressInfo ipInfo;
+        PdPrefixInfo pdInfo = null;
+        if (directConnFlag) {
+            // Add to host store if it is connected to network directly
+            ipInfo = extractIpAddress(dhcp6Packet);
+            if (ipInfo != null) {
+                if (isMsgRelease) {
+                    HostId hostId = HostId.hostId(srcMac, vlanId);
                     log.debug("remove Host {} ip for directly connected.", hostId.toString());
-                    // Remove host's ip of  when dhcp release msg is received
-                    providerService.removeIpFromHost(hostId, ip);
-                } else {
-                    log.debug("ipAddress not found. Do not add Host for directly connected.");
+                    providerService.removeIpFromHost(hostId, ipInfo.ip6Address);
                 }
             } else {
-                // Remove from route store if it is not connected to network directly
-                // pick out the first link-local ip address
-                IpAddress nextHopIp = getFirstIpByHost(clientMac, vlanId);
-                if (nextHopIp == null) {
-                    log.warn("Can't find link-local IP address of gateway mac {} vlanId {}",
-                            clientMac, vlanId);
-                    return;
-                }
+                log.debug("ipAddress not found. Do not remove Host {} for directly connected.",
+                        HostId.hostId(srcMac, vlanId).toString());
+            }
+            leafMsgType = dhcp6Packet.getMsgType();
+        } else {
+            // Remove from route store if it is not connected to network directly
+            // pick out the first link-local ip address
+            IpAddress nextHopIp = getFirstIpByHost(directConnFlag, srcMac, vlanId);
+            if (nextHopIp == null) {
+                log.warn("Can't find link-local IP address of gateway mac {} vlanId {}", srcMac, vlanId);
+                dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_GW);
+                return;
+            }
 
-                DHCP6 leafDhcp = getDhcp6Leaf(dhcp6Packet);
-                ip = extractIpAddress(leafDhcp);
-                if (ip == null) {
-                    log.debug("ip is null");
-                } else {
-                    Route routeForIP = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
+            DHCP6 leafDhcp = dhcp6HandlerUtil.getDhcp6Leaf(dhcp6Packet);
+            ipInfo = extractIpAddress(leafDhcp);
+            if (ipInfo == null) {
+                log.debug("ip is null");
+            } else {
+                if (isMsgRelease) {
+                    Route routeForIP = new Route(Route.Source.STATIC, ipInfo.ip6Address.toIpPrefix(), nextHopIp);
                     log.debug("removing route of 128 address for indirectly connected.");
-                    log.debug("128 ip {}, nexthop {}", HexString.toHexString(ip.toOctets(), ":"),
+                    log.debug("128 ip {}, nexthop {}",
+                            HexString.toHexString(ipInfo.ip6Address.toOctets(), ":"),
                             HexString.toHexString(nextHopIp.toOctets(), ":"));
                     routeStore.removeRoute(routeForIP);
                 }
+            }
 
-                IpPrefix ipPrefix = extractPrefix(leafDhcp);
-                if (ipPrefix == null) {
-                    log.debug("ipPrefix is null ");
-                } else {
-                    Route routeForPrefix = new Route(Route.Source.STATIC, ipPrefix, nextHopIp);
+            pdInfo = extractPrefix(leafDhcp);
+            if (pdInfo == null) {
+                log.debug("ipPrefix is null ");
+            } else {
+                if (isMsgRelease) {
+                    Route routeForPrefix = new Route(Route.Source.STATIC, pdInfo.pdPrefix, nextHopIp);
                     log.debug("removing route of PD for indirectly connected.");
-                    log.debug("pd ip {}, nexthop {}", HexString.toHexString(ipPrefix.address().toOctets(), ":"),
+                    log.debug("pd ip {}, nexthop {}",
+                            HexString.toHexString(pdInfo.pdPrefix.address().toOctets(), ":"),
                             HexString.toHexString(nextHopIp.toOctets(), ":"));
 
                     routeStore.removeRoute(routeForPrefix);
                     if (this.dhcpFpmEnabled) {
-                        dhcpFpmPrefixStore.removeFpmRecord(ipPrefix);
+                        dhcpFpmPrefixStore.removeFpmRecord(pdInfo.pdPrefix);
                     }
                 }
             }
+            leafMsgType = leafDhcp.getMsgType();
+       }
+
+        if (isMsgRelease) {
+            log.debug("DHCP6 RELEASE msg.");
+            if (record != null) {
+                if (ipInfo != null) {
+                    log.debug("DhcpRelay Record ip6Address is set to null.");
+                    record.ip6Address(null);
+                }
+                if (pdInfo != null) {
+                    log.debug("DhcpRelay Record pdPrefix is set to null.");
+                }
+
+                if (!record.ip6Address().isPresent() && !record.pdPrefix().isPresent()) {
+                    log.warn("IP6 address and IP6 PD both are null. Remove record.");
+                    // do not remove a record. Let timer task handler it.
+                    //dhcpRelayStore.removeDhcpRecord(HostId.hostId(leafClientMac, vlanId));
+                }
+            }
+        }
+
+        if (record != null) {
+            record.getV6Counters().incrementCounter(dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
+            record.addLocation(new HostLocation(location, System.currentTimeMillis()));
+            record.ip6Status(DHCP6.MsgType.getType(leafMsgType));
+            record.setDirectlyConnected(directConnFlag);
+            if (!directConnFlag) {
+                // Update gateway mac address if the host is not directly connected
+                record.nextHop(srcMac);
+            }
+            record.updateLastSeen();
+        }
+        dhcpRelayStore.updateDhcpRecord(leafHostId, record);
+        // TODO Use AtomicInteger for the counters
+        try {
+            recordSemaphore.acquire();
+            try {
+                dhcpRelayCountersStore.incrementCounter(gCount, dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
+            } finally {
+                // calling release() after a successful acquire()
+                recordSemaphore.release();
+            }
+        } catch (InterruptedException e) {
+            e.printStackTrace();
         }
     }
 
     /**
-     * add host or route.
+     * add host or route and update dhcp relay record.
      *
      * @param directConnFlag  flag to show that packet is from directly connected client
+     * @param location  client side connect point
      * @param dhcp6Relay the dhcp6 payload
-     * @param embeddedDhcp6 client's ethernet packetthe dhcp6 payload within relay
-     * @param clientMac client macAddress
+     * @param embeddedDhcp6 the dhcp6 payload within relay
+     * @param srcMac client gw/host macAddress
      * @param clientInterface client interface
      */
-    private void addHostOrRoute(boolean directConnFlag, DHCP6 dhcp6Relay,
-                                   DHCP6 embeddedDhcp6,
-                                   MacAddress clientMac,
-                                   Interface clientInterface) {
+    private void addHostOrRoute(boolean directConnFlag, ConnectPoint location, DHCP6 dhcp6Relay,
+                                DHCP6 embeddedDhcp6, MacAddress srcMac, Interface clientInterface) {
         log.debug("addHostOrRoute entered.");
         VlanId vlanId = clientInterface.vlan();
-        // add host or route
-        if (isDhcp6Reply(dhcp6Relay)) {
-            IpAddress ip = null;
-            if (directConnFlag) {
-                // Add to host store if it connect to network directly
-                ip = extractIpAddress(embeddedDhcp6);
-                if (ip != null) {
-                    Set<IpAddress> ips = Sets.newHashSet(ip);
+        Boolean isMsgReply = dhcp6HandlerUtil.isDhcp6Reply(dhcp6Relay);
+        MacAddress leafClientMac;
+        Byte leafMsgType;
 
-                    // FIXME: we should use vlan id from original packet (solicit, request)
-                    HostId hostId = HostId.hostId(clientMac, vlanId);
+        Dhcp6ClientIdOption clientIdOption = extractClinedId(directConnFlag, embeddedDhcp6);
+        if (clientIdOption != null) {
+            log.debug("CLIENTID option found {}", clientIdOption);
+            if ((clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LLT) ||
+                    (clientIdOption.getDuid().getDuidType() == Dhcp6Duid.DuidType.DUID_LL)) {
+                leafClientMac = MacAddress.valueOf(clientIdOption.getDuid().getLinkLayerAddress());
+            } else {
+                log.warn("Link-Layer Address not supported in CLIENTID option. No DhcpRelay Record created.");
+                dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_FAIL);
+                return;
+            }
+        } else {
+            log.warn("CLIENTID option NOT found. No DhcpRelay Record created.");
+            dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENTID_FAIL);
+            return;
+        }
+        HostId leafHostId = HostId.hostId(leafClientMac, vlanId);
+        DhcpRecord record = dhcpRelayStore.getDhcpRecord(leafHostId).orElse(null);
+        if (record == null) {
+            record = new DhcpRecord(HostId.hostId(leafClientMac, vlanId));
+        } else {
+            record = record.clone();
+        }
+
+        IpAddressInfo ipInfo;
+        PdPrefixInfo pdInfo = null;
+        if (directConnFlag) {
+            // Add to host store if it connect to network directly
+            ipInfo = extractIpAddress(embeddedDhcp6);
+            if (ipInfo != null) {
+                if (isMsgReply) {
+                    Set<IpAddress> ips = Sets.newHashSet(ipInfo.ip6Address);
+                    HostId hostId = HostId.hostId(srcMac, vlanId);
                     Host host = hostService.getHost(hostId);
                     HostLocation hostLocation = new HostLocation(clientInterface.connectPoint(),
-                                                                 System.currentTimeMillis());
+                            System.currentTimeMillis());
                     Set<HostLocation> hostLocations = Sets.newHashSet(hostLocation);
-
                     if (host != null) {
                         // Dual homing support:
                         // if host exists, use old locations and new location
                         hostLocations.addAll(host.locations());
                     }
-                    HostDescription desc = new DefaultHostDescription(clientMac, vlanId,
-                                                                      hostLocations, ips,
-                                                                      false);
+                    HostDescription desc = new DefaultHostDescription(srcMac, vlanId, hostLocations, ips,
+                            false);
                     log.debug("adding Host for directly connected.");
                     log.debug("client mac {} client vlan {} hostlocation {}",
-                            HexString.toHexString(clientMac.toBytes(), ":"),
-                            vlanId, hostLocation.toString());
-
+                            HexString.toHexString(srcMac.toBytes(), ":"), vlanId, hostLocation.toString());
                     // Replace the ip when dhcp server give the host new ip address
                     providerService.hostDetected(hostId, desc, false);
-                } else {
-                    log.debug("ipAddress not found. Do not add Host for directly connected.");
                 }
             } else {
-                // Add to route store if it does not connect to network directly
-                // pick out the first link-local ip address
-                IpAddress nextHopIp = getFirstIpByHost(clientMac, vlanId);
-                if (nextHopIp == null) {
-                    log.warn("Can't find link-local IP address of gateway mac {} vlanId {}",
-                            clientMac, vlanId);
-                    return;
-                }
+                log.warn("ipAddress not found. Do not add Host {} for directly connected.",
+                        HostId.hostId(srcMac, vlanId).toString());
+            }
+            leafMsgType = embeddedDhcp6.getMsgType();
+        } else {
+            // Add to route store if it does not connect to network directly
+            // pick out the first link-local ip address
+            IpAddress nextHopIp = getFirstIpByHost(directConnFlag, srcMac, vlanId);
+            if (nextHopIp == null) {
+                log.warn("Can't find link-local IP address of gateway mac {} vlanId {}", srcMac, vlanId);
+                dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_LINKLOCAL_GW);
+                return;
+            }
 
-                DHCP6 leafDhcp = getDhcp6Leaf(embeddedDhcp6);
-                ip = extractIpAddress(leafDhcp);
-                if (ip == null) {
-                    log.debug("ip is null");
-                } else {
-                    Route routeForIP = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
+            DHCP6 leafDhcp = dhcp6HandlerUtil.getDhcp6Leaf(embeddedDhcp6);
+            ipInfo = extractIpAddress(leafDhcp);
+            if (ipInfo == null) {
+                log.debug("ip is null");
+            } else {
+                if (isMsgReply) {
+                    Route routeForIP = new Route(Route.Source.STATIC, ipInfo.ip6Address.toIpPrefix(), nextHopIp);
                     log.debug("adding Route of 128 address for indirectly connected.");
                     routeStore.updateRoute(routeForIP);
                 }
+            }
 
-                IpPrefix ipPrefix = extractPrefix(leafDhcp);
-                if (ipPrefix == null) {
-                    log.debug("ipPrefix is null ");
-                } else {
-                    Route routeForPrefix = new Route(Route.Source.STATIC, ipPrefix, nextHopIp);
+            pdInfo = extractPrefix(leafDhcp);
+            if (pdInfo == null) {
+                log.debug("ipPrefix is null ");
+            } else {
+                if (isMsgReply) {
+                    Route routeForPrefix = new Route(Route.Source.STATIC, pdInfo.pdPrefix, nextHopIp);
                     log.debug("adding Route of PD for indirectly connected.");
                     routeStore.updateRoute(routeForPrefix);
                     if (this.dhcpFpmEnabled) {
-                        FpmRecord record = new FpmRecord(ipPrefix, nextHopIp, FpmRecord.Type.DHCP_RELAY);
-                        dhcpFpmPrefixStore.addFpmRecord(ipPrefix, record);
+                        FpmRecord fpmRecord = new FpmRecord(pdInfo.pdPrefix, nextHopIp, FpmRecord.Type.DHCP_RELAY);
+                        dhcpFpmPrefixStore.addFpmRecord(pdInfo.pdPrefix, fpmRecord);
                     }
                 }
             }
+            leafMsgType = leafDhcp.getMsgType();
+        }
+        if (leafMsgType == DHCP6.MsgType.RELEASE.value() ||
+                (leafMsgType == DHCP6.MsgType.REPLY.value()) && ipInfo == null) {
+            log.warn("DHCP6 RELEASE/REPLY(null ip) from Server. MsgType {}", leafMsgType);
+            //return;
+        }
+
+        record.addLocation(new HostLocation(location, System.currentTimeMillis()));
+
+        if (leafMsgType == DHCP6.MsgType.REPLY.value()) {
+            if (ipInfo != null) {
+                log.debug("IP6 address is being stored into dhcp-relay store.");
+                log.debug("IP6 address {}", HexString.toHexString(ipInfo.ip6Address.toOctets(), ":"));
+                record.ip6Address(ipInfo.ip6Address);
+                record.updateAddrPrefTime(ipInfo.prefTime);
+                record.updateLastIp6Update();
+            } else {
+                log.debug("IP6 address is not returned from server. Maybe only PD is returned.");
+            }
+            if (pdInfo != null) {
+                log.debug("IP6 PD address {}",
+                        HexString.toHexString(pdInfo.pdPrefix.address().toOctets(), ":"));
+                record.pdPrefix(pdInfo.pdPrefix);
+                record.updatePdPrefTime(pdInfo.prefTime);
+                record.updateLastPdUpdate();
+            } else {
+                log.debug("IP6 PD address is not returned from server. Maybe only IPAddress is returned.");
+            }
+        }
+
+        record.getV6Counters().incrementCounter(dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
+        record.ip6Status(DHCP6.MsgType.getType(leafMsgType));
+        record.setDirectlyConnected(directConnFlag);
+        record.updateLastSeen();
+        dhcpRelayStore.updateDhcpRecord(leafHostId, record);
+        // TODO Use AtomicInteger for the counters
+        try {
+            recordSemaphore.acquire();
+            try {
+                dhcpRelayCountersStore.incrementCounter(gCount, dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
+            } finally {
+                // calling release() after a successful acquire()
+                recordSemaphore.release();
+            }
+        } catch (InterruptedException e) {
+            e.printStackTrace();
         }
     }
 
     /**
-     * Build the DHCP6 solicit/request packet with gatewayip.
-     * TODO: method too long, need to be refactored.
+     * build the DHCP6 solicit/request packet with gatewayip.
      *
      * @param context packet context
      * @param clientPacket client ethernet packet
      * @param clientInterfaces set of client side interfaces
      */
-    private InternalPacket processDhcp6PacketFromClient(PacketContext context,
-                                                        Ethernet clientPacket, Set<Interface> clientInterfaces) {
+    private List<InternalPacket> processDhcp6PacketFromClient(PacketContext context,
+                                                              Ethernet clientPacket,
+                                                              Set<Interface> clientInterfaces) {
         ConnectPoint receivedFrom = context.inPacket().receivedFrom();
         DeviceId receivedFromDevice = receivedFrom.deviceId();
-        DhcpServerInfo serverInfo;
-        Ip6Address dhcpServerIp = null;
-        ConnectPoint dhcpServerConnectPoint = null;
-        MacAddress dhcpConnectMac = null;
-        VlanId dhcpConnectVlan = null;
-        Ip6Address dhcpGatewayIp = null;
-        Ip6Address indirectDhcpServerIp = null;
-        ConnectPoint indirectDhcpServerConnectPoint = null;
-        MacAddress indirectDhcpConnectMac = null;
-        VlanId indirectDhcpConnectVlan = null;
-        Ip6Address indirectDhcpGatewayIp = null;
-        Ip6Address indirectRelayAgentIpFromCfg = null;
-        if (!defaultServerInfoList.isEmpty()) {
-            serverInfo = defaultServerInfoList.get(0);
-            dhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
-            dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
-            dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
-            dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
-            dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
-        }
-        if (!indirectServerInfoList.isEmpty()) {
-            serverInfo = indirectServerInfoList.get(0);
-            indirectDhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
-            indirectDhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
-            indirectDhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
-            indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
-            indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
-            indirectRelayAgentIpFromCfg = serverInfo.getRelayAgentIp6(receivedFromDevice).orElse(null);
-        }
-        Ip6Address relayAgentIp = getRelayAgentIPv6Address(clientInterfaces);
+        Ip6Address relayAgentIp = dhcp6HandlerUtil.getRelayAgentIPv6Address(clientInterfaces);
         MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
         if (relayAgentIp == null || relayAgentMac == null) {
             log.warn("Missing DHCP relay agent interface Ipv6 addr config for "
-                             + "packet from client on port: {}. Aborting packet processing",
-                     clientInterfaces.iterator().next().connectPoint());
-            return null;
+                      + "packet from client on port: {}. Aborting packet processing",
+                      clientInterfaces.iterator().next().connectPoint());
+            dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
+            return Lists.newArrayList();
         }
-        // get dhcp6 header.
+
         IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
         UDP clientUdp = (UDP) clientIpv6.getPayload();
         DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
-        boolean directConnFlag = directlyConnected(clientDhcp6);
-        Interface serverInterface;
-        if (directConnFlag) {
-            serverInterface = getServerInterface();
-        } else {
-            serverInterface = getIndirectServerInterface();
-            if (serverInterface == null) {
-                // Indirect server interface not found, use default server interface
-                serverInterface = getServerInterface();
-            }
-        }
-        if (serverInterface == null) {
-            log.warn("Can't get {} server interface, ignore", directConnFlag ? "direct" : "indirect");
-            return null;
-        }
-        Ip6Address ipFacingServer = getFirstIpFromInterface(serverInterface);
-        MacAddress macFacingServer = serverInterface.mac();
-        if (ipFacingServer == null || macFacingServer == null) {
-            log.warn("No IP v6 address for server Interface {}", serverInterface);
-            return null;
-        }
-        Ethernet etherReply = clientPacket.duplicate();
-        etherReply.setSourceMACAddress(macFacingServer);
-        if ((directConnFlag && dhcpConnectMac == null)  ||
-                !directConnFlag && indirectDhcpConnectMac == null && dhcpConnectMac == null)   {
-            log.trace("Packet received from {} connected client.", directConnFlag ? "directly" : "indirectly");
-            log.debug("DHCP6 {} not yet resolved .. Aborting DHCP packet processing from client on port: {}",
-                     (dhcpGatewayIp == null) ? "server IP " + dhcpServerIp
-                             : "gateway IP " + dhcpGatewayIp,
-                     clientInterfaces.iterator().next().connectPoint());
-            return null;
-        }
-        if (dhcpServerConnectPoint == null) {
-            log.warn("DHCP6 server connection point direct {} directConn {} indirectConn {} is not set up yet",
-                     directConnFlag, dhcpServerConnectPoint, indirectDhcpServerConnectPoint);
-            return null;
-        }
 
-        etherReply.setDestinationMACAddress(dhcpConnectMac);
-        etherReply.setVlanID(dhcpConnectVlan.toShort());
-        IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
-        byte[] peerAddress = clientIpv6.getSourceAddress();
-        ipv6Packet.setSourceAddress(ipFacingServer.toOctets());
-        ipv6Packet.setDestinationAddress(dhcpServerIp.toOctets());
-        UDP udpPacket = (UDP) ipv6Packet.getPayload();
-        udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
-        DHCP6 dhcp6Packet = (DHCP6) udpPacket.getPayload();
-        byte[] dhcp6PacketByte = dhcp6Packet.serialize();
+        boolean directConnFlag = dhcp6HandlerUtil.directlyConnected(clientDhcp6);
 
         ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
         VlanId vlanIdInUse = VlanId.vlanId(clientPacket.getVlanID());
         Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
-                .stream().filter(iface -> interfaceContainsVlan(iface, vlanIdInUse))
-                .findFirst().orElse(null);
+                .stream().filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
+                .findFirst()
+                .orElse(null);
 
-        removeHostOrRoute(directConnFlag, dhcp6Packet, clientPacket, clientIpv6, clientInterface);
+        List<InternalPacket> internalPackets = new ArrayList<>();
+        List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
+        List<DhcpServerInfo> copyServerInfoList = new ArrayList<DhcpServerInfo>(serverInfoList);
 
-        DHCP6 dhcp6Relay = new DHCP6();
-        dhcp6Relay.setMsgType(DHCP6.MsgType.RELAY_FORW.value());
-        if (directConnFlag) {
-            dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
-            log.debug("direct connection: relayAgentIp obtained dynamically {}",
-                      HexString.toHexString(relayAgentIp.toOctets(), ":"));
+        for (DhcpServerInfo serverInfo : copyServerInfoList) {
+            if (!dhcp6HandlerUtil.checkDhcpServerConnPt(directConnFlag, serverInfo)) {
+                log.warn("Can't get server connect point, ignore");
+                continue;
+            }
+            DhcpServerInfo newServerInfo = getHostInfoForServerInfo(serverInfo, serverInfoList);
+            if (newServerInfo == null) {
+                log.warn("Can't get server interface with host info resolved, ignore");
+                continue;
+            }
 
-        } else {
-            if (indirectDhcpServerIp == null) {
-                log.debug("indirect DhcpServerIp not available, use default DhcpServerIp {}",
-                         HexString.toHexString(dhcpServerIp.toOctets()));
-             } else {
-                 // Indirect case, replace destination to indirect dhcp server if exist
-                 // Check if mac is obtained for valid server ip
-                 if (indirectDhcpConnectMac == null) {
-                     log.warn("DHCP6 {} not yet resolved .. Aborting DHCP "
-                                      + "packet processing from client on port: {}",
-                              (indirectDhcpGatewayIp == null) ? "server IP " + indirectDhcpServerIp
-                                      : "gateway IP " + indirectDhcpGatewayIp,
-                              clientInterfaces.iterator().next().connectPoint());
-                     return null;
-                 }
-                 etherReply.setDestinationMACAddress(indirectDhcpConnectMac);
-                 etherReply.setVlanID(indirectDhcpConnectVlan.toShort());
-                 ipv6Packet.setDestinationAddress(indirectDhcpServerIp.toOctets());
-             }
-             if (indirectRelayAgentIpFromCfg == null) {
-                 dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
-                 log.trace("indirect connection: relayAgentIp NOT availale from config file! Use dynamic. {}",
-                          HexString.toHexString(relayAgentIp.toOctets(), ":"));
-             } else {
-                 dhcp6Relay.setLinkAddress(indirectRelayAgentIpFromCfg.toOctets());
-                 log.trace("indirect connection: relayAgentIp from config file is available! {}",
-                           HexString.toHexString(indirectRelayAgentIpFromCfg.toOctets(), ":"));
-             }
-         }
-         // peer address: address of the client or relay agent from which
-         // the message to be relayed was received.
-         dhcp6Relay.setPeerAddress(peerAddress);
-         List<Dhcp6Option> options = new ArrayList<>();
-         // directly connected case, hop count is zero; otherwise, hop count + 1
-         if (directConnFlag) {
-             dhcp6Relay.setHopCount((byte) 0);
-         } else {
-             dhcp6Relay.setHopCount((byte) (dhcp6Packet.getHopCount() + 1));
-         }
-         // create relay message option
-         Dhcp6Option relayMessage = new Dhcp6Option();
-         relayMessage.setCode(DHCP6.OptionCode.RELAY_MSG.value());
-         relayMessage.setLength((short) dhcp6PacketByte.length);
-         relayMessage.setData(dhcp6PacketByte);
-         options.add(relayMessage);
-         // create interfaceId option
-         String inPortString = "-" + context.inPacket().receivedFrom().toString() + ":";
-         Dhcp6Option interfaceId = new Dhcp6Option();
-         interfaceId.setCode(DHCP6.OptionCode.INTERFACE_ID.value());
-         byte[] clientSoureMacBytes = clientPacket.getSourceMACAddress();
-         byte[] inPortStringBytes = inPortString.getBytes();
-         byte[] vlanIdBytes = new byte[2];
-         vlanIdBytes[0] = (byte) (clientPacket.getVlanID() & 0xff);
-         vlanIdBytes[1] = (byte) ((clientPacket.getVlanID() >> 8) & 0xff);
-         byte[] interfaceIdBytes = new byte[clientSoureMacBytes.length +
-                 inPortStringBytes.length + vlanIdBytes.length];
-         log.trace("Length: interfaceIdBytes  {} clientSoureMacBytes {} inPortStringBytes {} vlan {}",
-                   interfaceIdBytes.length, clientSoureMacBytes.length, inPortStringBytes.length,
-                   vlanIdBytes.length);
-         System.arraycopy(clientSoureMacBytes, 0, interfaceIdBytes, 0, clientSoureMacBytes.length);
-         System.arraycopy(inPortStringBytes, 0, interfaceIdBytes, clientSoureMacBytes.length, inPortStringBytes.length);
-         System.arraycopy(vlanIdBytes, 0, interfaceIdBytes, clientSoureMacBytes.length + inPortStringBytes.length,
-                          vlanIdBytes.length);
-         interfaceId.setData(interfaceIdBytes);
-         interfaceId.setLength((short) interfaceIdBytes.length);
-         options.add(interfaceId);
-         log.debug("interfaceId write srcMac {} portString {}",
-                   HexString.toHexString(clientSoureMacBytes, ":"), inPortString);
-         dhcp6Relay.setOptions(options);
-         udpPacket.setPayload(dhcp6Relay);
-         udpPacket.resetChecksum();
-         ipv6Packet.setPayload(udpPacket);
-         ipv6Packet.setHopLimit((byte) 64);
-         etherReply.setPayload(ipv6Packet);
-         if (directConnFlag || indirectDhcpServerIp == null) {
-             return new InternalPacket(etherReply, dhcpServerConnectPoint);
-         } else {
-             return new InternalPacket(etherReply, indirectDhcpServerConnectPoint);
-         }
-     }
+            Interface serverInterface = getServerInterface(newServerInfo);
+            if (serverInterface == null) {
+                log.warn("Can't get server interface, ignore");
+                continue;
+            }
+
+            Ethernet etherReply = dhcp6HandlerUtil.buildDhcp6PacketFromClient(context, clientPacket,
+                                                              clientInterfaces, newServerInfo, serverInterface);
+            removeHostOrRoute(directConnFlag, clientConnectionPoint, clientDhcp6, clientPacket,
+                    clientIpv6, clientInterface);
+
+            InternalPacket internalPacket = new Dhcp6HandlerUtil().new InternalPacket(etherReply,
+                    serverInfo.getDhcpServerConnectPoint().get());
+            internalPackets.add(internalPacket);
+        }
+        log.debug("num of client packets to send is{}", internalPackets.size());
+
+        return internalPackets;
+    }
 
     /**
-     *
      * process the DHCP6 relay-reply packet from dhcp server.
      *
      * @param context packet context
      * @param receivedPacket server ethernet packet
      * @param recevingInterfaces set of server side interfaces
+     * @return internalPacket toward client
      */
     private InternalPacket processDhcp6PacketFromServer(PacketContext context,
                                                         Ethernet receivedPacket, Set<Interface> recevingInterfaces) {
-
-        ConnectPoint receivedFrom = context.inPacket().receivedFrom();
-        DeviceId receivedFromDevice = receivedFrom.deviceId();
-
-        // TODO: refactor
-        DhcpServerInfo serverInfo;
-        Ip6Address dhcpServerIp = null;
-        ConnectPoint dhcpServerConnectPoint = null;
-        MacAddress dhcpConnectMac = null;
-        VlanId dhcpConnectVlan = null;
-        Ip6Address dhcpGatewayIp = null;
-
-        Ip6Address indirectDhcpServerIp = null;
-        ConnectPoint indirectDhcpServerConnectPoint = null;
-        MacAddress indirectDhcpConnectMac = null;
-        VlanId indirectDhcpConnectVlan = null;
-        Ip6Address indirectDhcpGatewayIp = null;
-        Ip6Address indirectRelayAgentIpFromCfg = null;
-
-        if (!defaultServerInfoList.isEmpty()) {
-            serverInfo = defaultServerInfoList.get(0);
-            dhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
-            dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
-            dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
-            dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
-            dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
-        }
-
-        if (!indirectServerInfoList.isEmpty()) {
-            serverInfo = indirectServerInfoList.get(0);
-            indirectDhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
-            indirectDhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
-            indirectDhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
-            indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
-            indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
-            indirectRelayAgentIpFromCfg = serverInfo.getRelayAgentIp6(receivedFromDevice).orElse(null);
-        }
-
         // get dhcp6 header.
         Ethernet etherReply = receivedPacket.duplicate();
         IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
         UDP udpPacket = (UDP) ipv6Packet.getPayload();
         DHCP6 dhcp6Relay = (DHCP6) udpPacket.getPayload();
+        Boolean directConnFlag = dhcp6HandlerUtil.directlyConnected(dhcp6Relay);
 
-        Boolean directConnFlag = directlyConnected(dhcp6Relay);
+        DHCP6 embeddedDhcp6 = dhcp6Relay.getOptions().stream()
+                .filter(opt -> opt instanceof Dhcp6RelayOption)
+                .map(BasePacket::getPayload)
+                .map(pld -> (DHCP6) pld)
+                .findFirst()
+                .orElse(null);
         ConnectPoint inPort = context.inPacket().receivedFrom();
-        if ((directConnFlag || (!directConnFlag && indirectDhcpServerIp == null))
-             && !inPort.equals(dhcpServerConnectPoint)) {
-            log.warn("Receiving port {} is not the same as server connect point {} for direct or indirect-null",
-                    inPort, dhcpServerConnectPoint);
-            return null;
-        }
+        DhcpServerInfo foundServerInfo = findServerInfoFromServer(directConnFlag, inPort);
 
-        if (!directConnFlag && indirectDhcpServerIp != null &&
-                                !inPort.equals(indirectDhcpServerConnectPoint)) {
-            log.warn("Receiving port {} is not the same as server connect point {} for indirect",
-                    inPort, indirectDhcpServerConnectPoint);
+        if (foundServerInfo == null) {
+            log.warn("Cannot find server info");
+            dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_SERVER_INFO);
             return null;
+        } else {
+            if (dhcp6HandlerUtil.isServerIpEmpty(foundServerInfo)) {
+                log.warn("Cannot find server info's ipaddress");
+                dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_SERVER_IP6ADDR);
+                return null;
+            }
         }
 
-
         Dhcp6InterfaceIdOption interfaceIdOption = dhcp6Relay.getOptions().stream()
                 .filter(opt -> opt instanceof Dhcp6InterfaceIdOption)
                 .map(opt -> (Dhcp6InterfaceIdOption) opt)
                 .findFirst()
                 .orElse(null);
-
         if (interfaceIdOption == null) {
             log.warn("Interface Id option is not present, abort packet...");
+            dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.OPTION_MISSING_FAIL);
             return null;
         }
 
         MacAddress peerMac = interfaceIdOption.getMacAddress();
         String clientConnectionPointStr = new String(interfaceIdOption.getInPort());
-
         ConnectPoint clientConnectionPoint = ConnectPoint.deviceConnectPoint(clientConnectionPointStr);
         VlanId vlanIdInUse = VlanId.vlanId(interfaceIdOption.getVlanId());
         Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
-                .stream()
-                .filter(iface -> interfaceContainsVlan(iface, vlanIdInUse))
-                .findFirst()
-                .orElse(null);
+                .stream().filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
+                .findFirst().orElse(null);
         if (clientInterface == null) {
             log.warn("Cannot get client interface for from packet, abort... vlan {}", vlanIdInUse.toString());
+            dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_MATCHING_INTF);
             return null;
         }
         MacAddress relayAgentMac = clientInterface.mac();
         if (relayAgentMac == null) {
             log.warn("Can not get client interface mac, abort packet..");
+            dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
             return null;
         }
         etherReply.setSourceMACAddress(relayAgentMac);
@@ -1084,13 +963,12 @@
             clientMac = clients.iterator().next().mac();
             if (clientMac == null) {
                 log.warn("No client mac address found, abort packet...");
+                dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_CLIENT_INTF_MAC);
                 return null;
             }
             log.trace("Client mac address found from getHostByIp");
-
         }
         etherReply.setDestinationMACAddress(clientMac);
-
         // ip header
         ipv6Packet.setSourceAddress(dhcp6Relay.getLinkAddress());
         ipv6Packet.setDestinationAddress(dhcp6Relay.getPeerAddress());
@@ -1101,39 +979,16 @@
         } else {
             udpPacket.setDestinationPort(UDP.DHCP_V6_SERVER_PORT);
         }
-
-        DHCP6 embeddedDhcp6 = dhcp6Relay.getOptions().stream()
-                    .filter(opt -> opt instanceof Dhcp6RelayOption)
-                    .map(BasePacket::getPayload)
-                    .map(pld -> (DHCP6) pld)
-                    .findFirst()
-                    .orElse(null);
-
-
         // add host or route
-        addHostOrRoute(directConnFlag, dhcp6Relay, embeddedDhcp6, clientMac, clientInterface);
+        addHostOrRoute(directConnFlag, clientConnectionPoint, dhcp6Relay, embeddedDhcp6, clientMac, clientInterface);
 
         udpPacket.setPayload(embeddedDhcp6);
         udpPacket.resetChecksum();
         ipv6Packet.setPayload(udpPacket);
         etherReply.setPayload(ipv6Packet);
-
-        return new InternalPacket(etherReply, clientConnectionPoint);
+        return new Dhcp6HandlerUtil().new InternalPacket(etherReply, clientConnectionPoint);
     }
 
-    // Returns the first v6 interface ip out of a set of interfaces or null.
-    // Checks all interfaces, and ignores v6 interface ips
-    private Ip6Address getRelayAgentIPv6Address(Set<Interface> intfs) {
-        for (Interface intf : intfs) {
-            for (InterfaceIpAddress ip : intf.ipAddressesList()) {
-                Ip6Address relayAgentIp = ip.ipAddress().getIp6Address();
-                if (relayAgentIp != null) {
-                    return relayAgentIp;
-                }
-            }
-        }
-        return null;
-    }
 
     @Override
     public void setDhcpFpmEnabled(Boolean enabled) {
@@ -1142,33 +997,37 @@
 
     @Override
     public void setDefaultDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
+        log.debug("setDefaultDhcpServerConfigs is called.");
         setDhcpServerConfigs(configs, defaultServerInfoList);
     }
 
     @Override
     public void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
+        log.debug("setIndirectDhcpServerConfigs is called.");
         setDhcpServerConfigs(configs, indirectServerInfoList);
     }
 
     public void setDhcpServerConfigs(Collection<DhcpServerConfig> configs, List<DhcpServerInfo> serverInfoList) {
+        log.debug("config size {}.", configs.size());
+
         if (configs.size() == 0) {
             // no config to update
             return;
         }
-
         // TODO: currently we pick up first DHCP server config.
         // Will use other server configs in the future for HA.
-        DhcpServerConfig serverConfig = configs.iterator().next();
-
-        if (!serverConfig.getDhcpServerIp6().isPresent()) {
-            // not a DHCPv6 config
-            return;
+        Boolean isConfigValid = false;
+        for (DhcpServerConfig serverConfig : configs) {
+            if (serverConfig.getDhcpServerIp6().isPresent()) {
+                isConfigValid = true;
+                break;
+            }
         }
-
-        if (!serverInfoList.isEmpty()) {
-            // remove old server info
-            DhcpServerInfo oldServerInfo = serverInfoList.remove(0);
-
+        if (!isConfigValid) {
+            log.warn("No IP V6 server address found.");
+            return;  // No IP V6 address found
+        }
+        for (DhcpServerInfo oldServerInfo : serverInfoList) {
             // stop monitoring gateway or server
             oldServerInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
                 hostService.stopMonitoringIp(gatewayIp);
@@ -1178,43 +1037,50 @@
                 cancelDhcpPacket(serverIp);
             });
         }
+        serverInfoList.clear();
+        for (DhcpServerConfig serverConfig : configs) {
+            // Create new server info according to the config
+            DhcpServerInfo newServerInfo = new DhcpServerInfo(serverConfig,
+                    DhcpServerInfo.Version.DHCP_V6);
+            checkState(newServerInfo.getDhcpServerConnectPoint().isPresent(),
+                    "Connect point not exists");
+            checkState(newServerInfo.getDhcpServerIp6().isPresent(),
+                    "IP of DHCP server not exists");
 
-        // Create new server info according to the config
-        DhcpServerInfo newServerInfo = new DhcpServerInfo(serverConfig,
-                                                          DhcpServerInfo.Version.DHCP_V6);
-        checkState(newServerInfo.getDhcpServerConnectPoint().isPresent(),
-                   "Connect point not exists");
-        checkState(newServerInfo.getDhcpServerIp6().isPresent(),
-                   "IP of DHCP server not exists");
+            log.debug("DHCP server connect point: {}", newServerInfo.getDhcpServerConnectPoint().orElse(null));
+            log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp6().orElse(null));
 
-        log.debug("DHCP server connect point: {}", newServerInfo.getDhcpServerConnectPoint().orElse(null));
-        log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp6().orElse(null));
+            Ip6Address serverIp = newServerInfo.getDhcpServerIp6().get();
+            Ip6Address ipToProbe;
+            if (newServerInfo.getDhcpGatewayIp6().isPresent()) {
+                ipToProbe = newServerInfo.getDhcpGatewayIp6().get();
+            } else {
+                ipToProbe = newServerInfo.getDhcpServerIp6().orElse(null);
+            }
+            String hostToProbe = newServerInfo.getDhcpGatewayIp6()
+                    .map(ip -> "gateway").orElse("server");
 
-        Ip6Address serverIp = newServerInfo.getDhcpServerIp6().get();
-        Ip6Address ipToProbe;
-        if (newServerInfo.getDhcpGatewayIp6().isPresent()) {
-            ipToProbe = newServerInfo.getDhcpGatewayIp6().get();
-        } else {
-            ipToProbe = newServerInfo.getDhcpServerIp6().orElse(null);
+            log.warn("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
+            hostService.startMonitoringIp(ipToProbe);
+
+            Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
+            if (!hosts.isEmpty()) {
+                Host host = hosts.iterator().next();
+                newServerInfo.setDhcpConnectVlan(host.vlan());
+                newServerInfo.setDhcpConnectMac(host.mac());
+                log.warn("Host found host {}", host);
+
+            } else {
+                log.warn("No host found host ip {}", ipToProbe);
+            }
+            // Add new server info
+            synchronized (this) {
+                serverInfoList.add(newServerInfo);
+            }
+            if (!hosts.isEmpty()) {
+                requestDhcpPacket(serverIp);
+            }
         }
-        String hostToProbe = newServerInfo.getDhcpGatewayIp6()
-                .map(ip -> "gateway").orElse("server");
-
-        log.debug("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
-        hostService.startMonitoringIp(ipToProbe);
-
-        Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
-        if (!hosts.isEmpty()) {
-            Host host = hosts.iterator().next();
-            newServerInfo.setDhcpConnectVlan(host.vlan());
-            newServerInfo.setDhcpConnectMac(host.mac());
-        }
-        // Add new server info
-        synchronized (this) {
-            serverInfoList.clear();
-            serverInfoList.add(0, newServerInfo);
-        }
-        requestDhcpPacket(serverIp);
     }
 
     class InternalHostListener implements HostListener {
@@ -1256,7 +1122,6 @@
             if (targetIp == null) {
                 targetIp = serverIp;
             }
-
             if (targetIp != null) {
                 if (host.ipAddresses().contains(targetIp)) {
                     serverInfo.setDhcpConnectMac(host.mac());
@@ -1290,7 +1155,6 @@
             if (targetIp == null) {
                 targetIp = serverIp;
             }
-
             if (targetIp != null) {
                 if (host.ipAddresses().contains(targetIp)) {
                     serverInfo.setDhcpConnectVlan(null);
@@ -1317,81 +1181,81 @@
                 .findFirst()
                 .orElse(null);
     }
+    /**
+     * Checks if serverInfo's host info (mac and vlan) is filled in; if not, fills in.
+     *
+     * @param serverInfo server information
+     * @return newServerInfo if host info can be either found or filled in.
+     */
+    private DhcpServerInfo getHostInfoForServerInfo(DhcpServerInfo serverInfo, List<DhcpServerInfo> sererInfoList) {
+        DhcpServerInfo newServerInfo = null;
+        MacAddress  dhcpServerConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
+        VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
+        ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
+
+        if (dhcpServerConnectMac != null && dhcpConnectVlan != null) {
+            newServerInfo = serverInfo;
+            log.info("DHCP server {} host info found. ConnectPt{}  Mac {} vlan {}", serverInfo.getDhcpServerIp6(),
+                    dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
+        } else {
+            log.warn("DHCP server {} not resolve yet connectPt {} mac {} vlan {}", serverInfo.getDhcpServerIp6(),
+                    dhcpServerConnectPoint, dhcpServerConnectMac, dhcpConnectVlan);
+
+            Ip6Address ipToProbe;
+            if (serverInfo.getDhcpGatewayIp6().isPresent()) {
+                ipToProbe = serverInfo.getDhcpGatewayIp6().get();
+            } else {
+                ipToProbe = serverInfo.getDhcpServerIp6().orElse(null);
+            }
+            String hostToProbe = serverInfo.getDhcpGatewayIp6()
+                    .map(ip -> "gateway").orElse("server");
+
+            log.info("Dynamically probing to resolve {} IP {}", hostToProbe, ipToProbe);
+            hostService.startMonitoringIp(ipToProbe);
+
+            Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
+            if (!hosts.isEmpty()) {
+                int serverInfoIndex = sererInfoList.indexOf(serverInfo);
+                Host host = hosts.iterator().next();
+                serverInfo.setDhcpConnectVlan(host.vlan());
+                serverInfo.setDhcpConnectMac(host.mac());
+                // replace the serverInfo in the list
+                sererInfoList.set(serverInfoIndex, serverInfo);
+                newServerInfo = serverInfo;
+                log.warn("Dynamically host found host {}", host);
+            } else {
+                log.warn("No host found host ip {} dynamically", ipToProbe);
+            }
+        }
+        return newServerInfo;
+    }
 
     /**
      * Gets Interface facing to the server for default host.
      *
+     * @param serverInfo server information
      * @return the Interface facing to the server; null if not found
      */
-    private Interface getServerInterface() {
-        DhcpServerInfo serverInfo;
-        ConnectPoint dhcpServerConnectPoint;
-        VlanId dhcpConnectVlan;
+    private Interface getServerInterface(DhcpServerInfo serverInfo) {
+        Interface serverInterface = null;
 
-        if (!defaultServerInfoList.isEmpty()) {
-            serverInfo = defaultServerInfoList.get(0);
-            dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
-            dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
+        ConnectPoint dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
+        VlanId dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
+
+        if (dhcpServerConnectPoint != null && dhcpConnectVlan != null) {
+        serverInterface = interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
+                    .stream()
+                    .filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
+                    .findFirst()
+                    .orElse(null);
         } else {
-            return null;
+            log.warn("DHCP server {} not resolve yet connectPoint {} vlan {}", serverInfo.getDhcpServerIp6(),
+                    dhcpServerConnectPoint, dhcpConnectVlan);
         }
-        if (dhcpServerConnectPoint == null || dhcpConnectVlan == null) {
-            log.info("Default DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
-            return null;
-        }
-        return interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
-                .stream()
-                .filter(iface -> interfaceContainsVlan(iface, dhcpConnectVlan))
-                .findFirst()
-                .orElse(null);
+
+        return serverInterface;
     }
 
-    /**
-     * Gets Interface facing to the server for indirect hosts.
-     * Use default server Interface if indirect server not configured.
-     *
-     * @return the Interface facing to the server; null if not found
-     */
-    private Interface getIndirectServerInterface() {
-        DhcpServerInfo serverInfo;
-
-        ConnectPoint indirectDhcpServerConnectPoint;
-        VlanId indirectDhcpConnectVlan;
-
-        if (!indirectServerInfoList.isEmpty()) {
-            serverInfo = indirectServerInfoList.get(0);
-            indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
-            indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
-        } else {
-            return getServerInterface();
-        }
-        if (indirectDhcpServerConnectPoint == null || indirectDhcpConnectVlan == null) {
-            log.info("Indirect DHCP server {} not resolve yet", serverInfo.getDhcpGatewayIp6());
-            return null;
-        }
-        return interfaceService.getInterfacesByPort(indirectDhcpServerConnectPoint)
-                .stream()
-                .filter(iface -> interfaceContainsVlan(iface, indirectDhcpConnectVlan))
-                .findFirst()
-                .orElse(null);
-    }
-
-    /**
-     * Determind if an Interface contains a vlan id.
-     *
-     * @param iface the Interface
-     * @param vlanId the vlan id
-     * @return true if the Interface contains the vlan id
-     */
-    private boolean interfaceContainsVlan(Interface iface, VlanId vlanId) {
-        if (vlanId.equals(VlanId.NONE)) {
-            // untagged packet, check if vlan untagged or vlan native is not NONE
-            return !iface.vlanUntagged().equals(VlanId.NONE) ||
-                    !iface.vlanNative().equals(VlanId.NONE);
-        }
-        // tagged packet, check if the interface contains the vlan
-        return iface.vlanTagged().contains(vlanId);
-    }
 
     private void requestDhcpPacket(Ip6Address serverIp) {
         requestServerDhcpPacket(serverIp);
@@ -1555,7 +1419,6 @@
             flowObjectiveService.apply(deviceId, fwd);
         });
     }
-
     /**
      * Find first ipaddress for a given Host info i.e.  mac and vlan.
      *
@@ -1563,7 +1426,7 @@
      * @param vlanId  packet's vlan
      * @return next-hop link-local ipaddress for a given host
      */
-    private IpAddress getFirstIpByHost(MacAddress clientMac, VlanId vlanId) {
+    private IpAddress getFirstIpByHost(Boolean directConnFlag, MacAddress clientMac, VlanId vlanId) {
         IpAddress nextHopIp;
         // pick out the first link-local ip address
         HostId gwHostId = HostId.hostId(clientMac, vlanId);
@@ -1572,13 +1435,157 @@
             log.warn("Can't find gateway host for hostId {}", gwHostId);
             return null;
         }
-        nextHopIp = gwHost.ipAddresses()
-                .stream()
-                .filter(IpAddress::isIp6)
-                .filter(IpAddress::isLinkLocal)
-                .map(IpAddress::getIp6Address)
-                .findFirst()
-                .orElse(null);
+        if (directConnFlag) {
+            nextHopIp = gwHost.ipAddresses()
+                    .stream()
+                    .filter(IpAddress::isIp6)
+                    .map(IpAddress::getIp6Address)
+                    .findFirst()
+                    .orElse(null);
+        } else {
+            nextHopIp = gwHost.ipAddresses()
+                    .stream()
+                    .filter(IpAddress::isIp6)
+                    .filter(ip6 -> ip6.isLinkLocal())
+                    .map(IpAddress::getIp6Address)
+                    .findFirst()
+                    .orElse(null);
+        }
         return nextHopIp;
     }
+
+    private List<DhcpServerInfo> findValidServerInfo(boolean directConnFlag) {
+        List<DhcpServerInfo> validServerInfo;
+
+        if (directConnFlag || indirectServerInfoList.isEmpty()) {
+            validServerInfo = new ArrayList<DhcpServerInfo>(defaultServerInfoList);
+        } else {
+            validServerInfo = new ArrayList<DhcpServerInfo>(indirectServerInfoList);
+        }
+        return validServerInfo;
+    }
+
+    private DhcpServerInfo findServerInfoFromServer(boolean directConnFlag, ConnectPoint inPort) {
+        List<DhcpServerInfo> validServerInfoList = findValidServerInfo(directConnFlag);
+        DhcpServerInfo  foundServerInfo = null;
+        for (DhcpServerInfo serverInfo : validServerInfoList) {
+            if (inPort.equals(serverInfo.getDhcpServerConnectPoint().get())) {
+                foundServerInfo = serverInfo;
+                log.warn("ServerInfo found for Rcv port {} Server Connect Point {} for {}",
+                        inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
+                break;
+            } else {
+                log.warn("Rcv port {} not the same as Server Connect Point {} for {}",
+                        inPort, serverInfo.getDhcpServerConnectPoint(), directConnFlag ? "direct" : "indirect");
+            }
+        }
+        return foundServerInfo;
+    }
+    /**
+     * Set the dhcp6 lease expiry poll interval value.
+     *
+     * @param val poll interval value in seconds
+     */
+    @Override
+    public void setDhcp6PollInterval(int val) {
+        dhcp6PollInterval = val;
+    }
+
+    /**
+     * get the dhcp6 lease expiry poll interval value.
+     * This is a private function
+     * @return  poll interval value in seconds
+     */
+    private int getDhcp6PollInterval() {
+        return dhcp6PollInterval;
+    }
+
+    /**
+     * Find lease-expired ipaddresses and pd prefixes.
+     * Removing host/route/fpm entries.
+     */
+    public void timeTick() {
+        long currentTime = System.currentTimeMillis();
+        Collection<DhcpRecord> records = dhcpRelayStore.getDhcpRecords();
+
+        log.debug("timeTick called currenttime {} records num {} ", currentTime, records.size());
+
+        records.forEach(record -> {
+                    boolean addrOrPdRemoved = false;
+                    DHCP6.MsgType ip6Status = record.ip6Status().orElse(null);
+                    if (ip6Status == null) {
+                        log.debug("record is not valid v6 record.");
+                        return;
+                    }
+
+                    if ((currentTime - record.getLastIp6Update()) >
+                            ((record.addrPrefTime() + getDhcp6PollInterval() / 2) * 1000)) {
+                        // remove ipaddress from host/route table
+                        IpAddress ip = record.ip6Address().orElse(null);
+                        if (ip != null) {
+                            if (record.directlyConnected()) {
+                                providerService.removeIpFromHost(HostId.hostId(record.macAddress(),
+                                        record.vlanId()), ip);
+                            } else {
+                                MacAddress gwMac = record.nextHop().orElse(null);
+                                if (gwMac == null) {
+                                    log.warn("Can't find gateway mac address from record {} for ip6Addr", record);
+                                    return;
+                                }
+                                IpAddress nextHopIp = getFirstIpByHost(record.directlyConnected(),
+                                        gwMac,
+                                        record.vlanId());
+                                Route route = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
+                                routeStore.removeRoute(route);
+                            }
+                            record.updateAddrPrefTime(0);
+                            record.ip6Address(null);
+                            addrOrPdRemoved = true;
+                            dhcpRelayStore.updateDhcpRecord(HostId.hostId(record.macAddress(),
+                                    record.vlanId()), record);
+                            log.warn("IP6 address is set to null. delta {} lastUpdate {} addrPrefTime {}",
+                                    (currentTime - record.getLastIp6Update()), record.getLastIp6Update(),
+                                    record.addrPrefTime());
+                        }
+                    }
+                    if ((currentTime - record.getLastPdUpdate()) >
+                            ((record.pdPrefTime() + getDhcp6PollInterval() / 2) * 1000)) {
+                        // remove PD from route/fpm table
+                        IpPrefix pdIpPrefix = record.pdPrefix().orElse(null);
+                        if (pdIpPrefix != null) {
+                            if (record.directlyConnected()) {
+                                providerService.removeIpFromHost(HostId.hostId(record.macAddress(), record.vlanId()),
+                                        pdIpPrefix.address().getIp6Address());
+                            } else {
+                                MacAddress gwMac = record.nextHop().orElse(null);
+                                if (gwMac == null) {
+                                    log.warn("Can't find gateway mac address from record {} for PD prefix", record);
+                                    return;
+                                }
+                                IpAddress nextHopIp = getFirstIpByHost(record.directlyConnected(),
+                                        gwMac,
+                                        record.vlanId());
+                                Route route = new Route(Route.Source.STATIC, pdIpPrefix, nextHopIp);
+                                routeStore.removeRoute(route);
+                                if (this.dhcpFpmEnabled) {
+                                    dhcpFpmPrefixStore.removeFpmRecord(pdIpPrefix);
+                                }
+                            }
+                            record.updatePdPrefTime(0);
+                            record.pdPrefix(null);
+                            addrOrPdRemoved = true;
+                            dhcpRelayStore.updateDhcpRecord(HostId.hostId(record.macAddress(),
+                                    record.vlanId()), record);
+                            log.warn("PD prefix is set to null.delta {} pdPrefTime {}",
+                                    (currentTime - record.getLastPdUpdate()), record.pdPrefTime());
+                        }
+                    }
+                    if (addrOrPdRemoved &&
+                            !record.ip6Address().isPresent() && !record.pdPrefix().isPresent()) {
+                        log.warn("ip6Status {} IP6 address and IP6 PD both are null. Remove record.", ip6Status);
+                        dhcpRelayStore.removeDhcpRecord(HostId.hostId(record.macAddress(), record.vlanId()));
+                    }
+                }
+        );
+    }
 }
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerUtil.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerUtil.java
new file mode 100644
index 0000000..6e47a0d
--- /dev/null
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerUtil.java
@@ -0,0 +1,485 @@
+/*
+ * 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.dhcprelay;
+
+import org.onlab.packet.BasePacket;
+import org.onlab.packet.DHCP6;
+import org.onlab.packet.DHCP6.MsgType;
+import org.onlab.packet.Ip6Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.VlanId;
+import org.onlab.packet.dhcp.Dhcp6RelayOption;
+import org.onlab.packet.dhcp.Dhcp6Option;
+
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv6;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.UDP;
+
+import org.onlab.util.HexString;
+import org.onosproject.dhcprelay.api.DhcpServerInfo;
+import org.onosproject.dhcprelay.store.DhcpRelayCounters;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.host.InterfaceIpAddress;
+import org.onosproject.net.intf.Interface;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.DeviceId;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import java.util.Set;
+import java.util.List;
+import java.util.ArrayList;
+
+
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+
+
+public class Dhcp6HandlerUtil {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+    // Returns the first v6 interface ip out of a set of interfaces or null.
+    // Checks all interfaces, and ignores v6 interface ips
+    public Ip6Address getRelayAgentIPv6Address(Set<Interface> intfs) {
+        for (Interface intf : intfs) {
+            for (InterfaceIpAddress ip : intf.ipAddressesList()) {
+                Ip6Address relayAgentIp = ip.ipAddress().getIp6Address();
+                if (relayAgentIp != null) {
+                    return relayAgentIp;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the first interface ip from interface.
+     *
+     * @param iface interface of one connect point
+     * @return the first interface IP; null if not exists an IP address in
+     *         these interfaces
+     */
+    public Ip6Address getFirstIpFromInterface(Interface iface) {
+        checkNotNull(iface, "Interface can't be null");
+        return iface.ipAddressesList().stream()
+                .map(InterfaceIpAddress::ipAddress)
+                .filter(IpAddress::isIp6)
+                .map(IpAddress::getIp6Address)
+                .findFirst()
+                .orElse(null);
+    }
+
+    /**
+     * extract DHCP6 payload from dhcp6 relay message within relay-forwrd/reply.
+     *
+     * @param dhcp6 dhcp6 relay-reply or relay-foward
+     * @return dhcp6Packet dhcp6 packet extracted from relay-message
+     */
+    public DHCP6 dhcp6PacketFromRelayPacket(DHCP6 dhcp6) {
+
+        // extract the relay message if exist
+        DHCP6 dhcp6Payload = dhcp6.getOptions().stream()
+                .filter(opt -> opt instanceof Dhcp6RelayOption)
+                .map(BasePacket::getPayload)
+                .map(pld -> (DHCP6) pld)
+                .findFirst()
+                .orElse(null);
+        if (dhcp6Payload == null) {
+            // Can't find dhcp payload
+            log.debug("Can't find dhcp6 payload from relay message");
+        } else {
+            log.debug("dhcp6 payload found from relay message {}", dhcp6Payload);
+        }
+        return dhcp6Payload;
+    }
+
+    /**
+     * find the leaf DHCP6 packet from multi-level relay packet.
+     *
+     * @param relayPacket dhcp6 relay packet
+     * @return leafPacket non-relay dhcp6 packet
+     */
+    public DHCP6 getDhcp6Leaf(DHCP6 relayPacket) {
+        DHCP6 dhcp6Parent = relayPacket;
+        DHCP6 dhcp6Child = null;
+
+        log.debug("getDhcp6Leaf entered.");
+        while (dhcp6Parent != null) {
+            dhcp6Child = dhcp6PacketFromRelayPacket(dhcp6Parent);
+            if (dhcp6Child != null) {
+                if (dhcp6Child.getMsgType() != DHCP6.MsgType.RELAY_FORW.value() &&
+                        dhcp6Child.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
+                    log.debug("leaf dhcp6 packet found.");
+                    break;
+                } else {
+                    // found another relay, go for another loop
+                    dhcp6Parent = dhcp6Child;
+                }
+            } else {
+                log.debug("Expected dhcp6 within relay pkt, but no dhcp6 leaf found.");
+                break;
+            }
+        }
+        return dhcp6Child;
+    }
+
+    /**
+     * check if DHCP6 relay-reply is reply.
+     *
+     * @param relayPacket dhcp6 relay-reply
+     * @return boolean relay-reply contains ack
+     */
+    public boolean isDhcp6Reply(DHCP6 relayPacket) {
+        DHCP6 leafDhcp6 = getDhcp6Leaf(relayPacket);
+        if (leafDhcp6 != null) {
+            if (leafDhcp6.getMsgType() == DHCP6.MsgType.REPLY.value()) {
+                log.debug("isDhcp6Reply  true.");
+                return true;  // must be directly connected
+            } else {
+                log.debug("isDhcp6Reply false. leaf dhcp6 is not replay. MsgType {}", leafDhcp6.getMsgType());
+            }
+        } else {
+            log.debug("isDhcp6Reply false. Expected dhcp6 within relay pkt but not found.");
+        }
+        log.debug("isDhcp6Reply  false.");
+        return false;
+    }
+
+    /**
+     * check if DHCP6 is release or relay-forward contains release.
+     *
+     * @param dhcp6Payload dhcp6 packet
+     * @return boolean dhcp6 contains release
+     */
+    public boolean isDhcp6Release(DHCP6 dhcp6Payload) {
+        if (dhcp6Payload.getMsgType() ==  DHCP6.MsgType.RELEASE.value()) {
+            log.debug("isDhcp6Release  true.");
+            return true;  // must be directly connected
+        } else {
+            DHCP6 dhcp6Leaf = getDhcp6Leaf(dhcp6Payload);
+            if (dhcp6Leaf != null) {
+                if (dhcp6Leaf.getMsgType() ==  DHCP6.MsgType.RELEASE.value()) {
+                    log.debug("isDhcp6Release  true. indirectlry connected");
+                    return true;
+                } else {
+                    log.debug("leaf dhcp6 is not release. MsgType {}",  dhcp6Leaf.getMsgType());
+                    return false;
+                }
+            } else {
+                log.debug("isDhcp6Release  false. dhcp6 is niether relay nor release.");
+                return false;
+            }
+        }
+    }
+
+
+    /**
+     * convert dhcp6 msgType to String.
+     *
+     * @param msgTypeVal msgType byte of dhcp6 packet
+     * @return String string value of dhcp6 msg type
+     */
+    public String getMsgTypeStr(byte msgTypeVal) {
+        MsgType msgType = DHCP6.MsgType.getType(msgTypeVal);
+        return DHCP6.MsgType.getMsgTypeStr(msgType);
+    }
+
+    /**
+     * find the string of dhcp6 leaf packets's msg type.
+     *
+     * @param directConnFlag boolean value indicating direct/indirect connection
+     * @param dhcp6Packet dhcp6 packet
+     * @return String string value of dhcp6 leaf packet msg type
+     */
+    public String findLeafMsgType(boolean directConnFlag, DHCP6  dhcp6Packet) {
+        if (directConnFlag) {
+            return getMsgTypeStr(dhcp6Packet.getMsgType());
+        } else {
+            DHCP6 leafDhcp = getDhcp6Leaf(dhcp6Packet);
+            if (leafDhcp != null) {
+                return getMsgTypeStr(leafDhcp.getMsgType());
+            } else {
+                return DhcpRelayCounters.INVALID_PACKET;
+            }
+        }
+    }
+
+    /**
+     * Determind if an Interface contains a vlan id.
+     *
+     * @param iface the Interface
+     * @param vlanId the vlan id
+     * @return true if the Interface contains the vlan id
+     */
+    public boolean interfaceContainsVlan(Interface iface, VlanId vlanId) {
+        if (vlanId.equals(VlanId.NONE)) {
+            // untagged packet, check if vlan untagged or vlan native is not NONE
+            return !iface.vlanUntagged().equals(VlanId.NONE) ||
+                    !iface.vlanNative().equals(VlanId.NONE);
+        }
+        // tagged packet, check if the interface contains the vlan
+        return iface.vlanTagged().contains(vlanId);
+    }
+
+    /**
+     * the new class the contains Ethernet packet and destination port.
+     */
+    public class InternalPacket {
+        Ethernet packet;
+        ConnectPoint destLocation;
+        public InternalPacket(Ethernet newPacket, ConnectPoint newLocation) {
+            packet = newPacket;
+            destLocation = newLocation;
+        }
+        void setLocation(ConnectPoint newLocation) {
+            destLocation = newLocation;
+        }
+    }
+    /**
+     * Check if the host is directly connected to the network or not.
+     *
+     * @param dhcp6Payload the dhcp6 payload
+     * @return true if the host is directly connected to the network; false otherwise
+     */
+    public boolean directlyConnected(DHCP6 dhcp6Payload) {
+        log.debug("directlyConnected enters");
+
+        if (dhcp6Payload.getMsgType() != DHCP6.MsgType.RELAY_FORW.value() &&
+                dhcp6Payload.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
+            log.debug("directlyConnected true. MsgType {}", dhcp6Payload.getMsgType());
+
+            return true;
+        }
+        // Regardless of relay-forward or relay-replay, check if we see another relay message
+        DHCP6 dhcp6Payload2 = dhcp6PacketFromRelayPacket(dhcp6Payload);
+        if (dhcp6Payload2 != null) {
+            if (dhcp6Payload.getMsgType() == DHCP6.MsgType.RELAY_FORW.value()) {
+                log.debug("directlyConnected  false. 1st realy-foward, 2nd MsgType {}", dhcp6Payload2.getMsgType());
+                return false;
+            } else {
+                // relay-reply
+                if (dhcp6Payload2.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
+                    log.debug("directlyConnected  true. 2nd MsgType {}", dhcp6Payload2.getMsgType());
+                    return true;  // must be directly connected
+                } else {
+                    log.debug("directlyConnected  false. 1st relay-reply, 2nd relay-reply MsgType {}",
+                            dhcp6Payload2.getMsgType());
+                    return false;  // must be indirectly connected
+                }
+            }
+        } else {
+            log.debug("directlyConnected  true.");
+            return true;
+        }
+    }
+    /**
+     * Check if a given server info has v6 ipaddress.
+     *
+     * @param serverInfo server info to check
+     * @return true if server info has v6 ip address; false otherwise
+     */
+    public boolean isServerIpEmpty(DhcpServerInfo serverInfo) {
+        if (!serverInfo.getDhcpServerIp6().isPresent()) {
+            log.warn("DhcpServerIp not available, use default DhcpServerIp {}",
+                    HexString.toHexString(serverInfo.getDhcpServerIp6().get().toOctets()));
+            return true;
+        }
+        return false;
+    }
+
+    private boolean isConnectMacEmpty(DhcpServerInfo serverInfo, Set<Interface> clientInterfaces) {
+        if (!serverInfo.getDhcpConnectMac().isPresent()) {
+            log.warn("DHCP6 {} not yet resolved .. Aborting DHCP "
+                            + "packet processing from client on port: {}",
+                    !serverInfo.getDhcpGatewayIp6().isPresent() ? "server IP " + serverInfo.getDhcpServerIp6()
+                            : "gateway IP " + serverInfo.getDhcpGatewayIp6(),
+                    clientInterfaces.iterator().next().connectPoint());
+            return true;
+        }
+        return false;
+    }
+
+    private boolean isRelayAgentIpFromCfgEmpty(DhcpServerInfo serverInfo, DeviceId receivedFromDevice) {
+        if (!serverInfo.getRelayAgentIp6(receivedFromDevice).isPresent()) {
+            log.warn("indirect connection: relayAgentIp NOT availale from config file! Use dynamic.");
+            return true;
+        }
+        return false;
+    }
+
+    private Dhcp6Option getInterfaceIdIdOption(PacketContext context, Ethernet clientPacket) {
+        String inPortString = "-" + context.inPacket().receivedFrom().toString() + ":";
+        Dhcp6Option interfaceId = new Dhcp6Option();
+        interfaceId.setCode(DHCP6.OptionCode.INTERFACE_ID.value());
+        byte[] clientSoureMacBytes = clientPacket.getSourceMACAddress();
+        byte[] inPortStringBytes = inPortString.getBytes();
+        byte[] vlanIdBytes = new byte[2];
+        vlanIdBytes[0] = (byte) (clientPacket.getVlanID() & 0xff);
+        vlanIdBytes[1] = (byte) ((clientPacket.getVlanID() >> 8) & 0xff);
+        byte[] interfaceIdBytes = new byte[clientSoureMacBytes.length +
+                inPortStringBytes.length + vlanIdBytes.length];
+        log.debug("Length: interfaceIdBytes  {} clientSoureMacBytes {} inPortStringBytes {} vlan {}",
+                interfaceIdBytes.length, clientSoureMacBytes.length, inPortStringBytes.length,
+                vlanIdBytes.length);
+
+        System.arraycopy(clientSoureMacBytes, 0, interfaceIdBytes, 0, clientSoureMacBytes.length);
+        System.arraycopy(inPortStringBytes, 0, interfaceIdBytes, clientSoureMacBytes.length,
+                inPortStringBytes.length);
+        System.arraycopy(vlanIdBytes, 0, interfaceIdBytes,
+                clientSoureMacBytes.length + inPortStringBytes.length,
+                vlanIdBytes.length);
+        interfaceId.setData(interfaceIdBytes);
+        interfaceId.setLength((short) interfaceIdBytes.length);
+        log.debug("interfaceId write srcMac {} portString {}",
+                HexString.toHexString(clientSoureMacBytes, ":"), inPortString);
+        return interfaceId;
+    }
+
+    private void addDhcp6OptionsFromClient(List<Dhcp6Option> options, byte[] dhcp6PacketByte,
+                                           PacketContext context, Ethernet clientPacket) {
+        Dhcp6Option relayMessage = new Dhcp6Option();
+        relayMessage.setCode(DHCP6.OptionCode.RELAY_MSG.value());
+        relayMessage.setLength((short) dhcp6PacketByte.length);
+        relayMessage.setData(dhcp6PacketByte);
+        options.add(relayMessage);
+        // create interfaceId option
+        Dhcp6Option interfaceId = getInterfaceIdIdOption(context, clientPacket);
+        options.add(interfaceId);
+    }
+
+    /**
+     * build the DHCP6 solicit/request packet with gatewayip.
+     *
+     * @param context packet context
+     * @param clientPacket client ethernet packet
+     * @param clientInterfaces set of client side interfaces
+     * @param serverInfo target server which a packet is generated for
+     * @param serverInterface target server interface
+     * @return ethernet packet with dhcp6 packet info
+     */
+    public Ethernet buildDhcp6PacketFromClient(PacketContext context, Ethernet clientPacket,
+                                               Set<Interface> clientInterfaces, DhcpServerInfo serverInfo,
+                                               Interface serverInterface) {
+        ConnectPoint receivedFrom = context.inPacket().receivedFrom();
+        DeviceId receivedFromDevice = receivedFrom.deviceId();
+
+        Ip6Address relayAgentIp = getRelayAgentIPv6Address(clientInterfaces);
+        MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
+        if (relayAgentIp == null || relayAgentMac == null) {
+            log.warn("Missing DHCP relay agent interface Ipv6 addr config for "
+                            + "packet from client on port: {}. Aborting packet processing",
+                    clientInterfaces.iterator().next().connectPoint());
+            return null;
+        }
+        IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
+        UDP clientUdp = (UDP) clientIpv6.getPayload();
+        DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
+        boolean directConnFlag = directlyConnected(clientDhcp6);
+
+        Ip6Address serverIpFacing = getFirstIpFromInterface(serverInterface);
+        if (serverIpFacing == null || serverInterface.mac() == null) {
+            log.warn("No IP v6 address for server Interface {}", serverInterface);
+            return null;
+        }
+
+        Ethernet etherReply = clientPacket.duplicate();
+        etherReply.setSourceMACAddress(serverInterface.mac());
+
+        // set default info and replace with indirect if available later on.
+        if (serverInfo.getDhcpConnectMac().isPresent()) {
+            etherReply.setDestinationMACAddress(serverInfo.getDhcpConnectMac().get());
+        }
+        if (serverInfo.getDhcpConnectVlan().isPresent()) {
+            etherReply.setVlanID(serverInfo.getDhcpConnectVlan().get().toShort());
+        }
+        IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
+        byte[] peerAddress = clientIpv6.getSourceAddress();
+        ipv6Packet.setSourceAddress(serverIpFacing.toOctets());
+        ipv6Packet.setDestinationAddress(serverInfo.getDhcpServerIp6().get().toOctets());
+        UDP udpPacket = (UDP) ipv6Packet.getPayload();
+        udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
+        DHCP6 dhcp6Packet = (DHCP6) udpPacket.getPayload();
+        byte[] dhcp6PacketByte = dhcp6Packet.serialize();
+
+        DHCP6 dhcp6Relay = new DHCP6();
+
+        dhcp6Relay.setMsgType(DHCP6.MsgType.RELAY_FORW.value());
+
+        if (directConnFlag) {
+            dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
+        } else {
+            if (isServerIpEmpty(serverInfo)) {
+                log.warn("indirect DhcpServerIp empty... use default server ");
+            } else {
+                // Indirect case, replace destination to indirect dhcp server if exist
+                // Check if mac is obtained for valid server ip
+                if (isConnectMacEmpty(serverInfo, clientInterfaces)) {
+                    log.warn("indirect Dhcp ConnectMac empty ...");
+                    return null;
+                }
+                etherReply.setDestinationMACAddress(serverInfo.getDhcpConnectMac().get());
+                etherReply.setVlanID(serverInfo.getDhcpConnectVlan().get().toShort());
+                ipv6Packet.setDestinationAddress(serverInfo.getDhcpServerIp6().get().toOctets());
+            }
+            if (isRelayAgentIpFromCfgEmpty(serverInfo, receivedFromDevice)) {
+                dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
+                log.debug("indirect connection: relayAgentIp NOT availale from config file! Use dynamic. {}",
+                        HexString.toHexString(relayAgentIp.toOctets(), ":"));
+            } else {
+                dhcp6Relay.setLinkAddress(serverInfo.getRelayAgentIp6(receivedFromDevice).get().toOctets());
+            }
+        }
+        // peer address: address of the client or relay agent from which the message to be relayed was received.
+        dhcp6Relay.setPeerAddress(peerAddress);
+        // directly connected case, hop count is zero; otherwise, hop count + 1
+        if (directConnFlag) {
+            dhcp6Relay.setHopCount((byte) 0);
+        } else {
+            dhcp6Relay.setHopCount((byte) (dhcp6Packet.getHopCount() + 1));
+        }
+
+        List<Dhcp6Option> options = new ArrayList<>();
+        addDhcp6OptionsFromClient(options, dhcp6PacketByte, context, clientPacket);
+        dhcp6Relay.setOptions(options);
+        udpPacket.setPayload(dhcp6Relay);
+        udpPacket.resetChecksum();
+        ipv6Packet.setPayload(udpPacket);
+        ipv6Packet.setHopLimit((byte) 64);
+        etherReply.setPayload(ipv6Packet);
+
+        return etherReply;
+    }
+
+    /**
+     * build the DHCP6 solicit/request packet with gatewayip.
+     *
+     * @param directConnFlag flag indicating if packet is from direct client or not
+     * @param serverInfo server to check its connect point
+     * @return boolean true if serverInfo is found; false otherwise
+     */
+    public boolean checkDhcpServerConnPt(boolean directConnFlag,
+                                          DhcpServerInfo serverInfo) {
+        if (serverInfo.getDhcpServerConnectPoint() == null) {
+            log.warn("DHCP6 server connect point for {} connPt {}",
+                    directConnFlag ? "direct" : "indirect", serverInfo.getDhcpServerConnectPoint());
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayManager.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayManager.java
index 07e5cd9..39123ac 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayManager.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayManager.java
@@ -93,6 +93,11 @@
 import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import static org.onlab.util.Tools.groupedThreads;
+
 
 import com.google.common.collect.ImmutableSet;
 
@@ -153,6 +158,7 @@
             }
     );
 
+
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected NetworkConfigRegistry cfgService;
 
@@ -192,14 +198,30 @@
             label = "Enable Address resolution protocol")
     protected boolean arpEnabled = true;
 
+    @Property(name = "dhcpPollInterval", intValue = 24 * 3600,
+            label = "dhcp relay poll interval")
+    protected int dhcpPollInterval = 24 * 3600;
+
     @Property(name = "dhcpFpmEnabled", boolValue = false,
             label = "Enable DhcpRelay Fpm")
     protected boolean dhcpFpmEnabled = false;
 
+
+    private ScheduledExecutorService timerExecutor;
+
     protected DeviceListener deviceListener = new InternalDeviceListener();
     private DhcpRelayPacketProcessor dhcpRelayPacketProcessor = new DhcpRelayPacketProcessor();
     private ApplicationId appId;
 
+    /**
+     *   One second timer.
+     */
+    class Dhcp6Timer implements Runnable {
+        @Override
+        public void run() {
+            v6Handler.timeTick();
+        }
+    };
 
     @Activate
     protected void activate(ComponentContext context) {
@@ -214,6 +236,14 @@
         //add the packet processor
         packetService.addProcessor(dhcpRelayPacketProcessor, PacketProcessor.director(0));
 
+        timerExecutor = Executors.newScheduledThreadPool(1,
+                groupedThreads("dhcpRelay",
+                        "config-reloader-%d", log));
+        timerExecutor.scheduleAtFixedRate(new Dhcp6Timer(),
+                0,
+                dhcpPollInterval,
+                TimeUnit.SECONDS);
+
         modified(context);
 
         // Enable distribute route store
@@ -222,6 +252,9 @@
         compCfgService.registerProperties(getClass());
 
         deviceService.addListener(deviceListener);
+
+
+
         log.info("DHCP-RELAY Started");
     }
 
@@ -233,6 +266,8 @@
         cancelArpPackets();
         compCfgService.unregisterProperties(getClass(), false);
         deviceService.removeListener(deviceListener);
+        timerExecutor.shutdown();
+
         log.info("DHCP-RELAY Stopped");
     }
 
@@ -254,6 +289,21 @@
             cancelArpPackets();
         }
 
+        int intervalVal = Tools.getIntegerProperty(properties, "dhcpPollInterval");
+        log.info("DhcpRelay poll interval new {} old {}", intervalVal, dhcpPollInterval);
+        if (intervalVal !=  dhcpPollInterval) {
+            timerExecutor.shutdown();
+            dhcpPollInterval = intervalVal;
+            timerExecutor = Executors.newScheduledThreadPool(1,
+                    groupedThreads("dhcpRelay",
+                            "config-reloader-%d", log));
+            timerExecutor.scheduleAtFixedRate(new Dhcp6Timer(),
+                        0,
+                        dhcpPollInterval > 1 ? dhcpPollInterval : 1,
+                        TimeUnit.SECONDS);
+            v6Handler.setDhcp6PollInterval(dhcpPollInterval);
+        }
+
         flag = Tools.isPropertyEnabled(properties, "dhcpFpmEnabled");
         if (flag != null) {
             boolean oldValue = dhcpFpmEnabled;
@@ -368,7 +418,10 @@
     public Collection<DhcpRecord> getDhcpRecords() {
         return dhcpRelayStore.getDhcpRecords();
     }
-
+    @Override
+    public void updateDhcpRecord(HostId hostId, DhcpRecord dhcpRecord) {
+        dhcpRelayStore.updateDhcpRecord(hostId, dhcpRecord);
+    }
     @Override
     public Optional<MacAddress> getDhcpServerMacAddress() {
         // TODO: depreated it
@@ -617,4 +670,6 @@
     public Optional<FpmRecord> removeFpmRecord(IpPrefix prefix) {
         return dhcpFpmPrefixStore.removeFpmRecord(prefix);
     }
+
+
 }
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/api/DhcpHandler.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/api/DhcpHandler.java
index 49b88e8..3de3ced 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/api/DhcpHandler.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/api/DhcpHandler.java
@@ -178,10 +178,22 @@
     void removeIgnoreVlanState(IgnoreDhcpConfig config);
 
     /**
+     * Hander for Dhcp expiration poll timer.
+     *
+     */
+    default void timeTick() { }
+
+    /**
+     * Update Dhcp expiration poll timer value.
+     *
+     * @param val the timer interval value
+     */
+    default void setDhcp6PollInterval(int val) { }
+
+    /**
      * Sets DHCP FPM Enable state.
      *
      * @param dhcpFpmFlag flag indicating dhcpFpmEnable state
      */
     default void setDhcpFpmEnabled(Boolean dhcpFpmFlag) { }
-
 }
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/api/DhcpRelayService.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/api/DhcpRelayService.java
index ef700d4..5eb8d9e 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/api/DhcpRelayService.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/api/DhcpRelayService.java
@@ -43,6 +43,14 @@
     Collection<DhcpRecord> getDhcpRecords();
 
     /**
+     * Updates DHCP record for specific host id (mac + vlan).
+     *
+     * @param hostId the id of host
+     * @param dhcpRecord the DHCP record of the host
+     */
+    void updateDhcpRecord(HostId hostId, DhcpRecord dhcpRecord);
+
+    /**
      * Gets mac address of DHCP server.
      *
      * @return the mac address of DHCP server; empty if not exist
@@ -102,4 +110,5 @@
      * @return boolean value
      */
     public boolean isDhcpFpmEnabled();
+
 }
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/cli/DhcpRelayAggCountersCommand.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/cli/DhcpRelayAggCountersCommand.java
new file mode 100644
index 0000000..a91a53a
--- /dev/null
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/cli/DhcpRelayAggCountersCommand.java
@@ -0,0 +1,82 @@
+/*
+ * 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.dhcprelay.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.dhcprelay.api.DhcpRelayService;
+import org.onosproject.dhcprelay.store.DhcpRelayCounters;
+import org.onosproject.dhcprelay.store.DhcpRelayCountersStore;
+
+
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * Prints Dhcp FPM Routes information.
+ */
+@Command(scope = "onos", name = "dhcp-relay-agg-counters",
+         description = "DHCP Relay Aggregate Counters cli.")
+public class DhcpRelayAggCountersCommand extends AbstractShellCommand {
+    @Argument(index = 0, name = "reset",
+            description = "reset counters or not",
+            required = false, multiValued = false)
+    String reset = null;
+
+    private static final String HEADER = "DHCP Relay Aggregate Counters :";
+    private static final String GCOUNT = "global";
+    private static final DhcpRelayService DHCP_RELAY_SERVICE = get(DhcpRelayService.class);
+
+    @Override
+    protected void execute() {
+        boolean toResetFlag;
+
+        if (reset != null) {
+            if (reset.equals("reset") || reset.equals("[reset]")) {
+                toResetFlag = true;
+            } else {
+                print("Last parameter is [reset]");
+                return;
+            }
+        } else {
+            toResetFlag = false;
+        }
+
+        print(HEADER);
+
+        DhcpRelayCountersStore counterStore = AbstractShellCommand.get(DhcpRelayCountersStore.class);
+
+        Optional<DhcpRelayCounters> perClassCounters = counterStore.getCounters(GCOUNT);
+
+        if (perClassCounters.isPresent()) {
+            Map<String, Integer> counters = perClassCounters.get().getCounters();
+            if (counters.size() > 0) {
+                counters.forEach((name, value) -> {
+                    print("%-30s  ............................  %-4d packets", name, value);
+                });
+            } else {
+                print("No counter for {}", GCOUNT);
+            }
+
+            if (toResetFlag) {
+                counterStore.resetCounters(GCOUNT);
+
+            }
+        }
+    }
+}
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/cli/DhcpRelayCommand.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/cli/DhcpRelayCommand.java
index bd41ecf..6bf46b8 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/cli/DhcpRelayCommand.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/cli/DhcpRelayCommand.java
@@ -16,12 +16,15 @@
 
 package org.onosproject.dhcprelay.cli;
 
+
+import org.apache.karaf.shell.commands.Argument;
 import org.apache.karaf.shell.commands.Command;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 import org.onlab.util.Tools;
 import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.dhcprelay.store.DhcpRelayCounters;
 import org.onosproject.dhcprelay.api.DhcpServerInfo;
 import org.onosproject.dhcprelay.api.DhcpRelayService;
 import org.onosproject.dhcprelay.store.DhcpRecord;
@@ -32,12 +35,30 @@
 import java.util.Collection;
 import java.util.List;
 import java.util.function.Predicate;
+import java.util.Map;
+
 
 /**
  * Prints DHCP server and DHCP relay status.
  */
 @Command(scope = "onos", name = "dhcp-relay", description = "DHCP relay app cli.")
 public class DhcpRelayCommand extends AbstractShellCommand {
+    @Argument(index = 0, name = "counter",
+            description = "shows counter values",
+            required = false, multiValued = false)
+    String counter = null;
+
+    @Argument(index = 1, name = "reset",
+            description = "reset counters or not",
+            required = false, multiValued = false)
+    String reset = null;
+
+
+
+    private static final String CONUTER_HEADER = "DHCP Relay Counters :";
+    private static final String COUNTER_HOST = "Counters for id=%s/%s, locations=%s%s";
+
+
     private static final String HEADER = "DHCP relay records ([D]: Directly connected):";
     private static final String NO_RECORDS = "No DHCP relay record found";
     private static final String HOST = "id=%s/%s, locations=%s%s, last-seen=%s, IPv4=%s, IPv6=%s";
@@ -49,12 +70,15 @@
     private static final String NA = "N/A";
     private static final String STATUS_FMT = "[%s, %s]";
     private static final String STATUS_FMT_NH = "[%s via %s, %s]";
+    private static final String STATUS_FMT_V6 = "[%s %d, %d ms %s %d %d ms, %s]";
+    private static final String STATUS_FMT_V6_NH = "[%s %d %d ms, %s %d %d ms via %s, %s]";
     private static final String DEFAULT_SERVERS = "Default DHCP servers:";
     private static final String INDIRECT_SERVERS = "Indirect DHCP servers:";
 
     private static final DhcpRelayService DHCP_RELAY_SERVICE = get(DhcpRelayService.class);
     private static final HostService HOST_SERVICE = get(HostService.class);
 
+
     @Override
     protected void execute() {
         List<DhcpServerInfo> defaultDhcpServerInfoList = DHCP_RELAY_SERVICE.getDefaultDhcpServerInfoList();
@@ -80,6 +104,52 @@
             print(NO_RECORDS);
             return;
         }
+
+        // Handle display of counters
+        boolean toResetFlag;
+
+        if (counter != null) {
+            if (counter.equals("counter") || reset.equals("[counter]")) {
+                print(CONUTER_HEADER);
+            } else {
+                print("first parameter is [counter]");
+                return;
+            }
+            if (reset != null) {
+                if (reset.equals("reset") || reset.equals("[reset]")) {
+                    toResetFlag = true;
+                } else {
+                    print("Last parameter is [reset]");
+                    return;
+                }
+            } else {
+                toResetFlag = false;
+            }
+
+            records.forEach(record -> {
+                print(COUNTER_HOST, record.macAddress(),
+                        record.vlanId(),
+                        record.locations(),
+                        record.directlyConnected() ? DIRECTLY : EMPTY);
+                DhcpRelayCounters v6Counters = record.getV6Counters();
+                Map<String, Integer> countersMap = v6Counters.getCounters();
+                countersMap.forEach((name, value) -> {
+                    print("%-30s  ............................  %-4d packets", name, value);
+                });
+                if (toResetFlag) {
+                    v6Counters.resetCounters();
+                    record.updateLastSeen();
+                    DHCP_RELAY_SERVICE.updateDhcpRecord(HostId.hostId(record.macAddress(), record.vlanId()), record);
+                }
+            });
+
+
+            return;
+        }
+
+
+        // Handle display of records
+
         print(HEADER);
         records.forEach(record -> print(HOST,
                                         record.macAddress(),
@@ -135,13 +205,30 @@
     }
 
     private String ip6State(DhcpRecord record) {
-        String nextHopIp = findNextHopIp(IpAddress::isIp6,
+        String nextHopIp = findNextHopIp6(IpAddress::isIp6,
                                          record.nextHop().orElse(null),
                                          record.vlanId());
-        return ipState(record.ip6Address().map(Object::toString).orElse(NA),
-                       record.ip6Status().map(Object::toString).orElse(NA),
-                       record.directlyConnected(),
-                       nextHopIp);
+
+        if (record.directlyConnected()) {
+            return String.format(STATUS_FMT_V6,
+                    record.ip6Address().map(Object::toString).orElse(NA),
+                    record.addrPrefTime(),
+                    record.getLastIp6Update(),
+                    record.pdPrefix().map(Object::toString).orElse(NA),
+                    record.pdPrefTime(),
+                    record.getLastPdUpdate(),
+                    record.ip6Status().map(Object::toString).orElse(NA));
+        } else {
+            return String.format(STATUS_FMT_V6_NH,
+                    record.ip6Address().map(Object::toString).orElse(NA),
+                    record.addrPrefTime(),
+                    record.getLastIp6Update(),
+                    record.pdPrefix().map(Object::toString).orElse(NA),
+                    record.pdPrefTime(),
+                    record.getLastPdUpdate(),
+                    nextHopIp,
+                    record.ip6Status().map(Object::toString).orElse(NA));
+        }
     }
 
     private String ipState(String ipAddress, String status,
@@ -158,6 +245,7 @@
         if (ipFilter == null || nextHopMac == null || vlanId == null) {
             return NA;
         }
+
         Host host = HOST_SERVICE.getHost(HostId.hostId(nextHopMac, vlanId));
         if (host == null) {
             return NA;
@@ -169,4 +257,21 @@
                 .findFirst()
                 .orElse(NA);
     }
+
+    private String findNextHopIp6(Predicate<IpAddress> ipFilter, MacAddress nextHopMac, VlanId vlanId) {
+        if (ipFilter == null || nextHopMac == null || vlanId == null) {
+            return NA;
+        }
+
+        Host host = HOST_SERVICE.getHost(HostId.hostId(nextHopMac, vlanId));
+        if (host == null) {
+            return NA;
+        }
+        return host.ipAddresses().stream()
+                .filter(ipFilter)
+                .filter(ip -> ip.isLinkLocal())
+                .map(Object::toString)
+                .findFirst()
+                .orElse(NA);
+    }
 }
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/cli/DhcpRelayCounterCompleter.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/cli/DhcpRelayCounterCompleter.java
new file mode 100644
index 0000000..8b024a5
--- /dev/null
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/cli/DhcpRelayCounterCompleter.java
@@ -0,0 +1,39 @@
+/*
+ * 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.dhcprelay.cli;
+
+import org.apache.karaf.shell.console.Completer;
+import org.apache.karaf.shell.console.completer.StringsCompleter;
+
+import java.util.List;
+import java.util.SortedSet;
+
+/**
+ * Dhcp Relay counter completer.
+ */
+public class DhcpRelayCounterCompleter implements Completer {
+
+    @Override
+    public int complete(String buffer, int cursor, List<String> candidates) {
+        // Delegate string completer
+        StringsCompleter delegate = new StringsCompleter();
+        SortedSet<String> strings = delegate.getStrings();
+        strings.add("counter");
+
+        // Now let the completer do the work for figuring out what to offer.
+        return delegate.complete(buffer, cursor, candidates);
+    }
+}
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/cli/DhcpRelayResetCompleter.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/cli/DhcpRelayResetCompleter.java
new file mode 100644
index 0000000..3eb4404
--- /dev/null
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/cli/DhcpRelayResetCompleter.java
@@ -0,0 +1,39 @@
+/*
+ * 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.dhcprelay.cli;
+
+import org.apache.karaf.shell.console.Completer;
+import org.apache.karaf.shell.console.completer.StringsCompleter;
+
+import java.util.List;
+import java.util.SortedSet;
+
+/**
+ * Dhcp Relay reset completer.
+ */
+public class DhcpRelayResetCompleter implements Completer {
+
+    @Override
+    public int complete(String buffer, int cursor, List<String> candidates) {
+        // Delegate string completer
+        StringsCompleter delegate = new StringsCompleter();
+        SortedSet<String> strings = delegate.getStrings();
+        strings.add("reset");
+
+        // Now let the completer do the work for figuring out what to offer.
+        return delegate.complete(buffer, cursor, candidates);
+    }
+}
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/store/DhcpRecord.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/store/DhcpRecord.java
index e133f03..44c6c3c 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/store/DhcpRecord.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/store/DhcpRecord.java
@@ -22,6 +22,7 @@
 import org.onlab.packet.DHCP6;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip6Address;
+import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 import org.onosproject.net.HostId;
@@ -49,10 +50,19 @@
     private DHCP.MsgType ip4Status;
 
     private Ip6Address ip6Address;
+    private IpPrefix pdPrefix;
     private DHCP6.MsgType ip6Status;
 
+
     private long lastSeen;
+    private long lastIp6Update;
+    private long lastPdUpdate;
+
     private boolean directlyConnected;
+    private long addrPrefTime;
+    private long pdPrefTime;
+    private DhcpRelayCounters v6Counters;
+
 
     /**
      * Creates a DHCP record for a host (mac + vlan).
@@ -67,6 +77,7 @@
         this.vlanId = hostId.vlanId();
         this.lastSeen = System.currentTimeMillis();
         this.directlyConnected = false;
+        this.v6Counters = new DhcpRelayCounters();
     }
 
     /**
@@ -159,6 +170,26 @@
     }
 
     /**
+     * Gets IPv6 PD address which assigned to the host.
+     *
+     * @return the PD IP address assigned to the host
+     */
+    public Optional<IpPrefix> pdPrefix() {
+        return Optional.ofNullable(pdPrefix);
+    }
+
+    /**
+     * Sets IPv6 PD address.
+     *
+     * @param pdPrefix the IPv6 PD address
+     * @return the DHCP record
+     */
+    public DhcpRecord pdPrefix(IpPrefix pdPrefix) {
+        this.pdPrefix = pdPrefix;
+        return this;
+    }
+
+    /**
      * Gets the latest time this record updated.
      *
      * @return the last time host send or receive DHCP packet
@@ -178,6 +209,84 @@
     }
 
     /**
+     * Gets the latest time this record updated with ip6 Address.
+     *
+     * @return the last time received DHCP packet provide ip6 Address
+     */
+    public long getLastIp6Update() {
+        return lastIp6Update;
+    }
+
+    /**
+     * Updates the update time of this record is given ip6 Address.
+     *
+     * @return the DHCP record
+     */
+    public DhcpRecord updateLastIp6Update() {
+        lastIp6Update = System.currentTimeMillis();
+        return this;
+    }
+
+    /**
+     * Gets the latest time this record updated with pd Prefix.
+     *
+     * @return the last time received DHCP packet provide pd Prefix
+     */
+    public long getLastPdUpdate() {
+        return lastPdUpdate;
+    }
+
+    /**
+     * Updates the update time of this record is given pd Prefix.
+     *
+     * @return the DHCP record
+     */
+    public DhcpRecord updateLastPdUpdate() {
+        lastPdUpdate = System.currentTimeMillis();
+        return this;
+    }
+
+    /**
+     * Gets the IP Address preferred time for this record.
+     *
+     * @return the preferred lease time for this ip address
+     */
+    public long addrPrefTime() {
+        return addrPrefTime;
+    }
+
+    /**
+     * Updates the IP Address preferred time of this record.
+     *
+     * @param prefTime preferred liftme
+     * @return the DHCP record
+     */
+    public DhcpRecord updateAddrPrefTime(long prefTime) {
+        addrPrefTime = prefTime;
+        return this;
+    }
+
+    /**
+     * Gets the PD Prefix preferred time for this record.
+     *
+     * @return the preferred lease time for this PD prefix
+     */
+    public long pdPrefTime() {
+        return pdPrefTime;
+    }
+
+    /**
+     * Updates the PD Prefix preferred time of this record.
+     *
+     * @param prefTime preferred liftme
+     * @return the DHCP record
+     */
+    public DhcpRecord updatePdPrefTime(long prefTime) {
+        pdPrefTime = prefTime;
+        return this;
+    }
+
+    /**
      * Indicated that the host is direct connected to the network or not.
      *
      * @return true if the host is directly connected to the network; false otherwise
@@ -279,6 +388,15 @@
     }
 
     /**
+     * Gets dhcp relay counters.
+     *
+     * @return the counter object
+     */
+    public DhcpRelayCounters getV6Counters() {
+        return v6Counters;
+    }
+
+    /**
      * Clone this DHCP record.
      *
      * @return the DHCP record which cloned
@@ -292,15 +410,23 @@
         newRecord.ip4Address = ip4Address;
         newRecord.ip4Status = ip4Status;
         newRecord.ip6Address = ip6Address;
+        newRecord.pdPrefix = pdPrefix;
         newRecord.ip6Status = ip6Status;
         newRecord.lastSeen = lastSeen;
+        newRecord.lastIp6Update = lastIp6Update;
+        newRecord.lastPdUpdate = lastPdUpdate;
+        newRecord.addrPrefTime = addrPrefTime;
+        newRecord.pdPrefTime = pdPrefTime;
+        newRecord.v6Counters = v6Counters;
+
         return newRecord;
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(locations, macAddress, vlanId, ip4Address, ip4Status,
-                            nextHop, nextHopTemp, ip6Address, ip6Status, lastSeen);
+                nextHop, nextHopTemp, ip6Address, pdPrefix, ip6Status, lastSeen,
+                lastIp6Update, lastPdUpdate, addrPrefTime, pdPrefTime, v6Counters);
     }
 
     @Override
@@ -320,9 +446,15 @@
                 Objects.equals(nextHop, that.nextHop) &&
                 Objects.equals(nextHopTemp, that.nextHopTemp) &&
                 Objects.equals(ip6Address, that.ip6Address) &&
+                Objects.equals(pdPrefix, that.pdPrefix) &&
                 Objects.equals(ip6Status, that.ip6Status) &&
                 Objects.equals(lastSeen, that.lastSeen) &&
-                Objects.equals(directlyConnected, that.directlyConnected);
+                Objects.equals(lastIp6Update, that.lastIp6Update) &&
+                Objects.equals(lastPdUpdate, that.lastPdUpdate) &&
+                Objects.equals(directlyConnected, that.directlyConnected) &&
+                Objects.equals(addrPrefTime, that.addrPrefTime) &&
+                Objects.equals(pdPrefTime, that.pdPrefTime) &&
+                Objects.equals(v6Counters, that.v6Counters);
     }
 
     @Override
@@ -336,9 +468,15 @@
                 .add("nextHop", nextHop)
                 .add("nextHopTemp", nextHopTemp)
                 .add("ip6Address", ip6Address)
+                .add("pdPrefix", pdPrefix)
                 .add("ip6State", ip6Status)
                 .add("lastSeen", lastSeen)
+                .add("lastIp6Update", lastIp6Update)
+                .add("lastPdUpdate", lastPdUpdate)
                 .add("directlyConnected", directlyConnected)
+                .add("addPrefTime", addrPrefTime)
+                .add("pdPrefTime", pdPrefTime)
+                .add("v6Counters", v6Counters)
                 .toString();
     }
 }
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/store/DhcpRelayCounters.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/store/DhcpRelayCounters.java
new file mode 100644
index 0000000..2a69c21
--- /dev/null
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/store/DhcpRelayCounters.java
@@ -0,0 +1,110 @@
+/*
+ * 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.dhcprelay.store;
+
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.Map;
+import java.util.Objects;
+
+public class DhcpRelayCounters {
+    // common counters
+
+    // IpV6 specific counters
+    public static final String SOLICIT = "SOLICIT";
+    public static final String ADVERTISE = "ADVERTISE";
+    public static final String REQUEST = "REQUEST";
+    public static final String CONFIRM = "CONFIRM";
+    public static final String RENEW = "RENEW";
+    public static final String REBIND = "REBIND";
+    public static final String REPLY = "REPLY";
+    public static final String RELEASE = "RELEASE";
+    public static final String DECLINE = "DECLINE";
+    public static final String RECONFIGURE = "RECONFIGURE";
+    public static final String INFORMATION_REQUEST = "INFORMATION_REQUEST";
+    public static final String RELAY_FORW = "RELAY_FORW";
+    public static final String RELAY_REPL = "RELAY_REPL";
+
+    public static final String NO_LINKLOCAL_GW = "No link-local in Gateway";
+    public static final String NO_LINKLOCAL_FAIL = "No link-local in CLIENT_ID";
+    public static final String NO_CLIENTID_FAIL = "No CLIENT_ID Found";
+    public static final String SVR_CFG_FAIL = "Server Config Error";
+    public static final String OPTION_MISSING_FAIL = "Expected Option missing";
+    public static final String NO_MATCHING_INTF = "No matching Inteface";
+    public static final String NO_CLIENT_INTF_MAC = "No client interface mac";
+    public static final String NO_SERVER_INFO = "No Server info found";
+    public static final String NO_SERVER_IP6ADDR = "No Server ip6 addr found";
+
+    public static final String INVALID_PACKET = "Invalid Packet";
+
+    public static final Set<String> SUPPORTED_COUNTERS =
+            ImmutableSet.of(SOLICIT, ADVERTISE, REQUEST, CONFIRM, RENEW,
+                    REBIND, REPLY, RELEASE, DECLINE, RECONFIGURE,
+                    INFORMATION_REQUEST, RELAY_FORW, RELAY_REPL,
+                    NO_LINKLOCAL_GW, NO_LINKLOCAL_FAIL, NO_CLIENTID_FAIL, SVR_CFG_FAIL, OPTION_MISSING_FAIL,
+                    NO_MATCHING_INTF, NO_CLIENT_INTF_MAC, NO_SERVER_INFO, NO_SERVER_IP6ADDR,
+                    INVALID_PACKET);
+
+    // TODO Use AtomicInteger for the counters
+    private Map<String, Integer> countersMap = new ConcurrentHashMap<>();
+    public long lastUpdate;
+
+    public void resetCounters() {
+        countersMap.forEach((name, value) -> {
+            countersMap.put(name, 0);
+        });
+    }
+    public boolean incrementCounter(String name) {
+        boolean counterValid = false;
+        if (SUPPORTED_COUNTERS.contains(name)) {
+            Integer counter = countersMap.get(name);
+            if (counter != null) {
+                counter = counter + 1;
+                countersMap.put(name, counter);
+            } else {
+                // this is the first time
+                countersMap.put(name, 1);
+            }
+            lastUpdate = System.currentTimeMillis();
+            counterValid = true;
+        }
+        return counterValid;
+    }
+    public Map<String, Integer> getCounters() {
+        return countersMap;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(countersMap, lastUpdate);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof DhcpRelayCounters)) {
+            return false;
+        }
+        DhcpRelayCounters that = (DhcpRelayCounters) obj;
+        return Objects.equals(countersMap, that.countersMap) &&
+                Objects.equals(lastUpdate, that.lastUpdate);
+    }
+}
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/store/DhcpRelayCountersStore.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/store/DhcpRelayCountersStore.java
new file mode 100644
index 0000000..3534871
--- /dev/null
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/store/DhcpRelayCountersStore.java
@@ -0,0 +1,65 @@
+/*
+ * 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.dhcprelay.store;
+
+
+import java.util.Optional;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Stores DHCP Relay Counters records.
+ */
+public interface DhcpRelayCountersStore {
+
+    /**
+     * Creates or updates DHCP record for specific host id (mac + vlan).
+     *
+     * @param counterClass class of counters (direct, indirect, global)
+     * @param counterName name of counter
+     */
+    void incrementCounter(String counterClass, String counterName);
+
+    /**
+     * Gets the DHCP counter record for a given counter class.
+     *
+     * @param counterClass the class of counters (direct, indirect, global)
+     * @return the DHCP counter record for a given counter class; empty if record not exists
+     */
+    Optional<DhcpRelayCounters> getCounters(String counterClass);
+
+    /**
+     * Gets all classes of DHCP counters record from store.
+     *
+     * @return all classes of DHCP counters records from store
+     */
+    Set<Map.Entry<String, DhcpRelayCounters>> getAllCounters();
+
+    /**
+     * Resets counter value for a given counter class.
+     *
+     * @param counterClass the class of counters (direct, indirect, global)
+     */
+    void resetCounters(String counterClass);
+
+    /**
+     * Resets counter value for a all counter classes.
+     *
+     */
+    void resetAllCounters();
+
+}
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/store/DistributedDhcpRelayCountersStore.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/store/DistributedDhcpRelayCountersStore.java
new file mode 100644
index 0000000..4b7b26c
--- /dev/null
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/store/DistributedDhcpRelayCountersStore.java
@@ -0,0 +1,130 @@
+/*
+ * 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.dhcprelay.store;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+
+import org.onlab.util.KryoNamespace;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.onosproject.store.serializers.KryoNamespaces;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import org.onosproject.store.service.Versioned;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+@Component(immediate = true)
+@Service
+public class DistributedDhcpRelayCountersStore implements DhcpRelayCountersStore {
+    private static final KryoNamespace.Builder APP_KYRO = KryoNamespace.newBuilder()
+            .register(KryoNamespaces.API)
+            .register(DhcpRelayCounters.class);
+
+    private Logger log = LoggerFactory.getLogger(getClass());
+    private ConsistentMap<String, DhcpRelayCounters> counters;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected StorageService storageService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+
+    @Activate
+    protected void activated() {
+        ApplicationId appId = coreService.getAppId("org.onosproject.Dhcp6HandlerImpl");
+        counters = storageService.<String, DhcpRelayCounters>consistentMapBuilder()
+                .withSerializer(Serializer.using(APP_KYRO.build()))
+                .withName("Dhcp-Relay-Counters")
+                .withApplicationId(appId)
+                .withPurgeOnUninstall()
+                .build();
+    }
+
+    @Deactivate
+    protected void deactivated() {
+        counters.destroy().join();
+    }
+    @Override
+    public void incrementCounter(String coutnerClass, String counterName) {
+        DhcpRelayCounters countersRecord;
+
+        Versioned<DhcpRelayCounters> vCounters = counters.get(coutnerClass);
+        if (vCounters == null) {
+            countersRecord = new DhcpRelayCounters();
+        } else {
+            countersRecord = vCounters.value();
+        }
+        countersRecord.incrementCounter(counterName);
+        counters.put(coutnerClass, countersRecord);
+    }
+
+    @Override
+    public Set<Map.Entry<String, DhcpRelayCounters>> getAllCounters() {
+        final Set<Map.Entry<String, DhcpRelayCounters>> result =
+                new HashSet<Map.Entry<String, DhcpRelayCounters>>();
+        Set<Map.Entry<String, Versioned<DhcpRelayCounters>>> tmpCounters = counters.entrySet();
+        tmpCounters.forEach(entry -> {
+            String key = entry.getKey();
+            DhcpRelayCounters value = entry.getValue().value();
+            ConcurrentHashMap<String, DhcpRelayCounters> newMap = new ConcurrentHashMap();
+            newMap.put(key, value);
+
+            for (Map.Entry m: newMap.entrySet()) {
+                result.add(m);
+            }
+        });
+        return  result;
+    }
+    @Override
+    public Optional<DhcpRelayCounters> getCounters(String counterClass) {
+        DhcpRelayCounters countersRecord;
+        checkNotNull(counterClass, "counter class can't be null");
+        Versioned<DhcpRelayCounters> vCounters = counters.get(counterClass);
+        if (vCounters == null) {
+            return Optional.empty();
+        }
+        return Optional.of(vCounters.value());
+    }
+    @Override
+    public void resetAllCounters() {
+        counters.clear();
+    }
+
+    @Override
+    public void resetCounters(String counterClass) {
+        checkNotNull(counterClass, "counter class can't be null");
+        DhcpRelayCounters countersRecord = counters.get(counterClass).value();
+        countersRecord.resetCounters();
+        counters.put(counterClass, countersRecord);
+    }
+
+}
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/store/DistributedDhcpRelayStore.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/store/DistributedDhcpRelayStore.java
index 38c0580..884d9cc 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/store/DistributedDhcpRelayStore.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/store/DistributedDhcpRelayStore.java
@@ -52,6 +52,7 @@
             .register(DhcpRecord.class)
             .register(DHCP.MsgType.class)
             .register(DHCP6.MsgType.class)
+            .register(DhcpRelayCounters.class)
             .build();
 
     private Logger log = getLogger(getClass());
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/store/DistributedFpmPrefixStore.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/store/DistributedFpmPrefixStore.java
index b82aac9..bc95d0b 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/store/DistributedFpmPrefixStore.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/store/DistributedFpmPrefixStore.java
@@ -91,7 +91,7 @@
 
     @Override
     public void setDelegate(StoreDelegate<FpmPrefixStoreEvent> delegate) {
-        checkNotNull("Delegate can't be null", delegate);
+        checkNotNull(delegate, "Delegate can't be null");
         this.delegate = delegate;
     }
 
@@ -122,6 +122,7 @@
      * @param prefix the route prefix in the advertisement
      * @param fpmRecord the route for fpm
      **/
+    @Override
     public void addFpmRecord(IpPrefix prefix, FpmRecord fpmRecord) {
         checkNotNull(prefix, "Prefix can't be null");
         checkNotNull(fpmRecord, "Fpm record can't be null");
@@ -134,6 +135,7 @@
      * @param prefix the route prefix in the advertisement
      * @return none
      **/
+    @Override
     public Optional<FpmRecord> removeFpmRecord(IpPrefix prefix) {
         checkNotNull(prefix, "Prefix can't be null");
         return Optional.ofNullable(dhcpFpmRecords.remove(prefix));
diff --git a/apps/dhcprelay/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/dhcprelay/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index b178195..c0a4b6f 100644
--- a/apps/dhcprelay/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/apps/dhcprelay/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -18,6 +18,10 @@
     <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
         <command>
             <action class="org.onosproject.dhcprelay.cli.DhcpRelayCommand"/>
+            <completers>
+                <ref component-id="dhcpRelayCounterCompleter"/>
+                <ref component-id="dhcpRelayResetCompleter"/>
+            </completers>
         </command>
         <command>
             <action class="org.onosproject.dhcprelay.cli.DhcpFpmRoutesCommand"/>
@@ -28,5 +32,13 @@
         <command>
             <action class="org.onosproject.dhcprelay.cli.DhcpFpmDeleteCommand"/>
         </command>
+        <command>
+            <action class="org.onosproject.dhcprelay.cli.DhcpRelayAggCountersCommand"/>
+            <completers>
+                <ref component-id="dhcpRelayResetCompleter"/>
+            </completers>
+        </command>
     </command-bundle>
+    <bean id="dhcpRelayCounterCompleter" class="org.onosproject.dhcprelay.cli.DhcpRelayCounterCompleter"/>
+    <bean id="dhcpRelayResetCompleter" class="org.onosproject.dhcprelay.cli.DhcpRelayResetCompleter"/>
 </blueprint>
diff --git a/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/DhcpRelayManagerTest.java b/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/DhcpRelayManagerTest.java
index 74e1379..65518ee 100644
--- a/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/DhcpRelayManagerTest.java
+++ b/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/DhcpRelayManagerTest.java
@@ -54,6 +54,13 @@
 import org.onlab.packet.dhcp.Dhcp6IaAddressOption;
 import org.onlab.packet.dhcp.Dhcp6IaPrefixOption;
 import org.onlab.packet.dhcp.Dhcp6Option;
+import org.onlab.packet.dhcp.Dhcp6ClientIdOption;
+import org.onlab.packet.dhcp.Dhcp6Duid;
+import org.onosproject.dhcprelay.store.DhcpRelayStore;
+import org.onosproject.dhcprelay.store.DhcpRecord;
+import org.onosproject.dhcprelay.store.DhcpRelayStoreEvent;
+import org.onosproject.dhcprelay.store.DhcpRelayCounters;
+import org.onosproject.dhcprelay.store.DhcpRelayCountersStore;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.TestApplicationId;
 import org.onosproject.cfg.ComponentConfigService;
@@ -63,9 +70,6 @@
 import org.onosproject.dhcprelay.config.DhcpServerConfig;
 import org.onosproject.dhcprelay.config.IgnoreDhcpConfig;
 import org.onosproject.dhcprelay.config.IndirectDhcpRelayConfig;
-import org.onosproject.dhcprelay.store.DhcpRecord;
-import org.onosproject.dhcprelay.store.DhcpRelayStore;
-import org.onosproject.dhcprelay.store.DhcpRelayStoreEvent;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.behaviour.Pipeliner;
@@ -253,6 +257,7 @@
     private MockPacketService packetService;
     private MockRouteStore mockRouteStore;
     private MockDhcpRelayStore mockDhcpRelayStore;
+    private MockDhcpRelayCountersStore mockDhcpRelayCountersStore;
     private HostProviderService mockHostProviderService;
     private FlowObjectiveService flowObjectiveService;
     private DeviceService deviceService;
@@ -322,9 +327,11 @@
 
         mockRouteStore = new MockRouteStore();
         mockDhcpRelayStore = new MockDhcpRelayStore();
-        manager.dhcpRelayStore = mockDhcpRelayStore;
-        manager.deviceService = deviceService;
+        mockDhcpRelayCountersStore = new MockDhcpRelayCountersStore();
 
+        manager.dhcpRelayStore = mockDhcpRelayStore;
+
+        manager.deviceService = deviceService;
 
         manager.interfaceService = new MockInterfaceService();
         flowObjectiveService = EasyMock.niceMock(FlowObjectiveService.class);
@@ -344,6 +351,7 @@
 
         v6Handler = new Dhcp6HandlerImpl();
         v6Handler.dhcpRelayStore = mockDhcpRelayStore;
+        v6Handler.dhcpRelayCountersStore = mockDhcpRelayCountersStore;
         v6Handler.hostService = manager.hostService;
         v6Handler.interfaceService = manager.interfaceService;
         v6Handler.packetService = manager.packetService;
@@ -358,6 +366,7 @@
         // properties
         Dictionary<String, Object> dictionary = createNiceMock(Dictionary.class);
         expect(dictionary.get("arpEnabled")).andReturn(true).anyTimes();
+        expect(dictionary.get("dhcpPollInterval")).andReturn(120).anyTimes();
         ComponentContext context = createNiceMock(ComponentContext.class);
         expect(context.getProperties()).andReturn(dictionary).anyTimes();
 
@@ -1001,6 +1010,48 @@
         }
     }
 
+    private class MockDhcpRelayCountersStore implements DhcpRelayCountersStore {
+        private Map<String, DhcpRelayCounters> counters = Maps.newHashMap();
+
+        public void incrementCounter(String coutnerClass, String counterName) {
+            DhcpRelayCounters countersRecord;
+
+            DhcpRelayCounters classCounters = counters.get(coutnerClass);
+            if (classCounters == null) {
+                classCounters = new DhcpRelayCounters();
+            }
+            classCounters.incrementCounter(counterName);
+            counters.put(coutnerClass, classCounters);
+        }
+
+        @Override
+        public Set<Map.Entry<String, DhcpRelayCounters>> getAllCounters() {
+            return counters.entrySet();
+        }
+
+        @Override
+        public Optional<DhcpRelayCounters> getCounters(String counterClass) {
+            DhcpRelayCounters classCounters = counters.get(counterClass);
+            if (classCounters == null) {
+                return Optional.empty();
+            }
+            return Optional.of(classCounters);
+        }
+
+        @Override
+        public void resetAllCounters() {
+            counters.clear();
+        }
+
+        @Override
+        public void resetCounters(String counterClass) {
+            DhcpRelayCounters classCounters = counters.get(counterClass);
+            classCounters.resetCounters();
+            counters.put(counterClass, classCounters);
+        }
+    }
+
+
     private class MockPacketService extends PacketServiceAdapter {
         Set<PacketProcessor> packetProcessors = Sets.newHashSet();
         OutboundPacket emittedPacket;
@@ -1233,6 +1284,14 @@
         iaAddressOption.setValidLifetime(1200);
         iaAddressOption.setLength((short) Dhcp6IaAddressOption.DEFAULT_LEN);
 
+        Dhcp6ClientIdOption clientIdOption = new Dhcp6ClientIdOption();
+        Dhcp6Duid dhcp6Duip = new Dhcp6Duid();
+        dhcp6Duip.setDuidType(Dhcp6Duid.DuidType.DUID_LLT);
+        dhcp6Duip.setHardwareType((short) 0x01);   // Ethernet
+        dhcp6Duip.setDuidTime(1234);
+        dhcp6Duip.setLinkLayerAddress(CLIENT_MAC.toBytes());
+        clientIdOption.setDuid(dhcp6Duip);
+
         Dhcp6IaNaOption iaNaOption = new Dhcp6IaNaOption();
         iaNaOption.setCode(DHCP6.OptionCode.IA_NA.value());
         iaNaOption.setIaId(0);
@@ -1265,6 +1324,7 @@
         dhcp6.setMsgType(msgType);
         List<Dhcp6Option> dhcp6Options = new ArrayList<Dhcp6Option>();
         dhcp6Options.add(iaNaOption);
+        dhcp6Options.add(clientIdOption);
         dhcp6Options.add(iaPdOption);
         dhcp6.setOptions(dhcp6Options);
 
diff --git a/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/store/DhcpRecordTest.java b/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/store/DhcpRecordTest.java
index ec6ad5c..7aaf65b 100644
--- a/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/store/DhcpRecordTest.java
+++ b/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/store/DhcpRecordTest.java
@@ -86,6 +86,12 @@
 
         TestUtils.setField(record, "lastSeen", 0);
         TestUtils.setField(record2, "lastSeen", 0);
+        TestUtils.setField(record, "addrPrefTime", 0);
+        TestUtils.setField(record2, "addrPrefTime", 0);
+        TestUtils.setField(record, "pdPrefTime", 0);
+        TestUtils.setField(record2, "pdPrefTime", 0);
+        TestUtils.setField(record, "v6Counter", null);
+        TestUtils.setField(record2, "v6Counter", null);
 
         assertThat(record, equalTo(record2));
         assertThat(record.hashCode(), equalTo(record2.hashCode()));
diff --git a/apps/evpn-route-service/api/src/main/java/org/onosproject/evpnrouteservice/EvpnInstanceRoute.java b/apps/evpn-route-service/api/src/main/java/org/onosproject/evpnrouteservice/EvpnInstanceRoute.java
index 3e98531..ca0c8bd8 100644
--- a/apps/evpn-route-service/api/src/main/java/org/onosproject/evpnrouteservice/EvpnInstanceRoute.java
+++ b/apps/evpn-route-service/api/src/main/java/org/onosproject/evpnrouteservice/EvpnInstanceRoute.java
@@ -198,7 +198,7 @@
 
         EvpnInstanceRoute that = (EvpnInstanceRoute) other;
 
-        return Objects.equals(prefix, prefix)
+        return Objects.equals(prefix, that.prefix)
                 && Objects.equals(nextHop, that.nextHop)
                 && Objects.equals(evpnName, that.evpnName)
                 && Objects.equals(rd, that.rd)
diff --git a/apps/evpn-route-service/api/src/main/java/org/onosproject/evpnrouteservice/EvpnRoute.java b/apps/evpn-route-service/api/src/main/java/org/onosproject/evpnrouteservice/EvpnRoute.java
index 9449093..0f025d3 100644
--- a/apps/evpn-route-service/api/src/main/java/org/onosproject/evpnrouteservice/EvpnRoute.java
+++ b/apps/evpn-route-service/api/src/main/java/org/onosproject/evpnrouteservice/EvpnRoute.java
@@ -115,7 +115,6 @@
         checkNotNull(prefixMac);
         checkNotNull(prefix);
         //checkNotNull(nextHop); //next hop can be null in case of MP un reach.
-        checkNotNull(labelToInt);
         this.source = checkNotNull(source);
         this.prefix = prefix;
         this.prefixMac = prefixMac;
@@ -258,7 +257,7 @@
 
         EvpnRoute that = (EvpnRoute) other;
 
-        return Objects.equals(prefixMac, prefixMac)
+        return Objects.equals(prefixMac, that.prefixMac)
                 && Objects.equals(prefix, that.prefix)
                 && Objects.equals(nextHop, that.nextHop)
                 && Objects.equals(this.rd, that.rd)
diff --git a/apps/evpn-route-service/app/src/main/java/org/onosproject/evpnrouteservice/impl/EvpnRouteManager.java b/apps/evpn-route-service/app/src/main/java/org/onosproject/evpnrouteservice/impl/EvpnRouteManager.java
index 19e6311..28abb7d 100644
--- a/apps/evpn-route-service/app/src/main/java/org/onosproject/evpnrouteservice/impl/EvpnRouteManager.java
+++ b/apps/evpn-route-service/app/src/main/java/org/onosproject/evpnrouteservice/impl/EvpnRouteManager.java
@@ -81,7 +81,9 @@
     @Deactivate
     protected void deactivate() {
         evpnRouteStore.unsetDelegate(evpnRouteStoreDelegate);
-        listeners.values().forEach(EvpnListenerQueue::stop);
+        synchronized (this) {
+            listeners.values().forEach(EvpnListenerQueue::stop);
+        }
     }
 
     /**
@@ -148,6 +150,7 @@
     }
 
 
+    @Override
     public Collection<EvpnRouteTableId> getRouteTables() {
         return evpnRouteStore.getRouteTables();
     }
diff --git a/apps/evpnopenflow/src/main/java/org/onosproject/evpnopenflow/rsc/vpnport/impl/VpnPortManager.java b/apps/evpnopenflow/src/main/java/org/onosproject/evpnopenflow/rsc/vpnport/impl/VpnPortManager.java
index 18ff172..2babba6 100644
--- a/apps/evpnopenflow/src/main/java/org/onosproject/evpnopenflow/rsc/vpnport/impl/VpnPortManager.java
+++ b/apps/evpnopenflow/src/main/java/org/onosproject/evpnopenflow/rsc/vpnport/impl/VpnPortManager.java
@@ -290,8 +290,6 @@
         String cidr = "0.0.0.0/0";
         String gatewayIp = "0.0.0.0";
         Set<HostRoute> hostRoutes = Sets.newHashSet();
-        String ipV6AddressMode = null;
-        String ipV6RaMode = null;
         TenantNetworkId tenantNetworkId = null;
         Set<AllocationPool> allocationPools = Sets.newHashSet();
         Iterable<TenantNetwork> networks
@@ -306,11 +304,11 @@
         Subnet subnet = new DefaultSubnet(SubnetId.subnetId(id), subnetName,
                                           tenantNetworkId,
                                           tenantId, IpAddress.Version.INET,
-                                          cidr == null ? null : IpPrefix.valueOf(cidr),
-                                          gatewayIp == null ? null : IpAddress.valueOf(gatewayIp),
+                                          IpPrefix.valueOf(cidr),
+                                          IpAddress.valueOf(gatewayIp),
                                           false, false, hostRoutes,
-                                          ipV6AddressMode == null ? null : Subnet.Mode.valueOf(ipV6AddressMode),
-                                          ipV6RaMode == null ? null : Subnet.Mode.valueOf(ipV6RaMode),
+                                          null,
+                                          null,
                                           allocationPools);
 
         Set<Subnet> subnetsSet = Sets.newHashSet(subnet);
diff --git a/apps/faultmanagement/fmmgr/src/test/java/org/onosproject/faultmanagement/impl/PollingAlarmProviderTest.java b/apps/faultmanagement/fmmgr/src/test/java/org/onosproject/faultmanagement/impl/PollingAlarmProviderTest.java
index b8fa17f..b2f2469 100644
--- a/apps/faultmanagement/fmmgr/src/test/java/org/onosproject/faultmanagement/impl/PollingAlarmProviderTest.java
+++ b/apps/faultmanagement/fmmgr/src/test/java/org/onosproject/faultmanagement/impl/PollingAlarmProviderTest.java
@@ -120,7 +120,7 @@
 
     @Test
     public void activate() throws Exception {
-        assertFalse("Provider should be registered", providerRegistry.getProviders().contains(provider));
+        assertFalse("Provider should be registered", providerRegistry.getProviders().contains(provider.id()));
         assertEquals("Device listener should be added", 1, deviceListeners.size());
         assertEquals("Incorrect alarm provider service", alarmProviderService, provider.providerService);
         assertEquals("Mastership listener should be added", 1, mastershipListeners.size());
@@ -135,7 +135,7 @@
         provider.deactivate();
         assertEquals("Device listener should be removed", 0, deviceListeners.size());
         assertEquals("Mastership listener should be removed", 0, mastershipListeners.size());
-        assertFalse("Provider should not be registered", providerRegistry.getProviders().contains(provider));
+        assertFalse("Provider should not be registered", providerRegistry.getProviders().contains(provider.id()));
         assertTrue(provider.alarmsExecutor.isShutdown());
         assertNull(provider.providerService);
     }
diff --git a/apps/faultmanagement/fmweb/src/test/java/org/onosproject/faultmanagement/web/AlarmsWebResourceTest.java b/apps/faultmanagement/fmweb/src/test/java/org/onosproject/faultmanagement/web/AlarmsWebResourceTest.java
index 94315c7..778c23f 100644
--- a/apps/faultmanagement/fmweb/src/test/java/org/onosproject/faultmanagement/web/AlarmsWebResourceTest.java
+++ b/apps/faultmanagement/fmweb/src/test/java/org/onosproject/faultmanagement/web/AlarmsWebResourceTest.java
@@ -20,7 +20,6 @@
 import org.junit.Test;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
-import org.onlab.rest.BaseResource;
 import org.onosproject.codec.CodecService;
 import org.onosproject.codec.impl.CodecManager;
 import org.onosproject.rest.resources.ResourceTest;
@@ -48,7 +47,7 @@
                 // Currently no alarms-service implemented
                 // .add(AlarmsService.class, alarmsService)
                 .add(CodecService.class, codecService);
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
     }
 
     @Test
diff --git a/apps/gluon/src/main/java/org/onosproject/gluon/rsc/GluonServer.java b/apps/gluon/src/main/java/org/onosproject/gluon/rsc/GluonServer.java
index f4f400e..031ba69 100644
--- a/apps/gluon/src/main/java/org/onosproject/gluon/rsc/GluonServer.java
+++ b/apps/gluon/src/main/java/org/onosproject/gluon/rsc/GluonServer.java
@@ -86,10 +86,10 @@
 
 public class GluonServer {
 
-    private static String protonKeyUri;
-    private static String serverUri;
+    private String protonKeyUri;
+    private String serverUri;
 
-    private static CloseableHttpAsyncClient httpClient;
+    private CloseableHttpAsyncClient httpClient;
 
     //store gluon server supported subkeys
     private List<String> subKeys = new LinkedList<>();
diff --git a/apps/graphitemetrics/src/test/java/org/onosproject/graphitemetrics/GraphiteMetricsReporterTest.java b/apps/graphitemetrics/src/test/java/org/onosproject/graphitemetrics/GraphiteMetricsReporterTest.java
index 5ecaf28..5036964 100644
--- a/apps/graphitemetrics/src/test/java/org/onosproject/graphitemetrics/GraphiteMetricsReporterTest.java
+++ b/apps/graphitemetrics/src/test/java/org/onosproject/graphitemetrics/GraphiteMetricsReporterTest.java
@@ -49,13 +49,6 @@
     }
 
     /**
-     * Tears down graphite metrics reporter instance.
-     */
-    public void tearDown() {
-        gmr.deactivate();
-    }
-
-    /**
      * Tests whether the containsName method can always return the correct result
      * with the given metric name and a set of prefixes.
      */
diff --git a/apps/influxdbmetrics/src/main/java/org/onosproject/influxdbmetrics/DefaultInfluxMetric.java b/apps/influxdbmetrics/src/main/java/org/onosproject/influxdbmetrics/DefaultInfluxMetric.java
index f3ce81a..bcee522 100644
--- a/apps/influxdbmetrics/src/main/java/org/onosproject/influxdbmetrics/DefaultInfluxMetric.java
+++ b/apps/influxdbmetrics/src/main/java/org/onosproject/influxdbmetrics/DefaultInfluxMetric.java
@@ -67,7 +67,6 @@
 
         @Override
         public InfluxMetric build() {
-            checkNotNull(oneMinRate, ONE_MIN_RATE_MSG);
             checkNotNull(timestamp, TIMESTAMP_MSG);
 
             return new DefaultInfluxMetric(oneMinRate, parseTime(timestamp));
diff --git a/apps/linkprops/src/main/java/org/onosproject/linkprops/LinkPropsTopovMessageHandler.java b/apps/linkprops/src/main/java/org/onosproject/linkprops/LinkPropsTopovMessageHandler.java
index 908c65c..a633ec0 100644
--- a/apps/linkprops/src/main/java/org/onosproject/linkprops/LinkPropsTopovMessageHandler.java
+++ b/apps/linkprops/src/main/java/org/onosproject/linkprops/LinkPropsTopovMessageHandler.java
@@ -248,27 +248,26 @@
      */
     private Highlights getBandwidth(Set<Link> links, DeviceId devId) {
         LpLinkMap linkMap = new LpLinkMap();
+        Highlights highlights = new Highlights();
         if (links != null) {
             log.debug("Processing {} links", links.size());
             links.forEach(linkMap::add);
+
+            PortNumber portnum = PortNumber.portNumber((int) links.iterator().next().src().port().toLong());
+
+            for (LpLink dlink : linkMap.biLinks()) {
+                DiscreteResourceId parent = Resources.discrete(devId, portnum).id();
+                ContinuousResource continuousResource =
+                        (ContinuousResource) resourceQueryService.getAvailableResources(parent,
+                                Bandwidth.class).iterator().next();
+                double availBandwidth = continuousResource.value();
+
+                dlink.makeImportant().setLabel(Double.toString(availBandwidth) + " bytes/s");
+                highlights.add(dlink.highlight(null));
+            }
         } else {
             log.debug("No egress links found for device {}", devId);
         }
-
-        Highlights highlights = new Highlights();
-
-        PortNumber portnum = PortNumber.portNumber((int) links.iterator().next().src().port().toLong());
-
-        for (LpLink dlink : linkMap.biLinks()) {
-            DiscreteResourceId parent = Resources.discrete(devId, portnum).id();
-            ContinuousResource continuousResource =
-                    (ContinuousResource) resourceQueryService.getAvailableResources(parent,
-                                                                                    Bandwidth.class).iterator().next();
-            double availBandwidth = continuousResource.value();
-
-            dlink.makeImportant().setLabel(Double.toString(availBandwidth) + " bytes/s");
-            highlights.add(dlink.highlight(null));
-        }
         return highlights;
     }
 
diff --git a/apps/mappingmanagement/api/src/main/java/org/onosproject/mapping/addresses/UnresolvedExtensionMappingAddress.java b/apps/mappingmanagement/api/src/main/java/org/onosproject/mapping/addresses/UnresolvedExtensionMappingAddress.java
index a1db59f..3a07879 100644
--- a/apps/mappingmanagement/api/src/main/java/org/onosproject/mapping/addresses/UnresolvedExtensionMappingAddress.java
+++ b/apps/mappingmanagement/api/src/main/java/org/onosproject/mapping/addresses/UnresolvedExtensionMappingAddress.java
@@ -18,8 +18,6 @@
 import org.onosproject.net.flow.AbstractExtension;
 
 import java.util.Arrays;
-import java.util.Objects;
-
 import static com.google.common.base.MoreObjects.toStringHelper;
 import static org.onosproject.mapping.addresses.ExtensionMappingAddressType
                              .ExtensionMappingAddressTypes.UNRESOLVED_TYPE;
@@ -63,7 +61,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(bytes);
+        return Arrays.hashCode(bytes);
     }
 
     @Override
diff --git a/apps/mappingmanagement/web/src/test/java/org/onosproject/mapping/web/MappingsWebResourceTest.java b/apps/mappingmanagement/web/src/test/java/org/onosproject/mapping/web/MappingsWebResourceTest.java
index d2f4c86..4d5f06d 100644
--- a/apps/mappingmanagement/web/src/test/java/org/onosproject/mapping/web/MappingsWebResourceTest.java
+++ b/apps/mappingmanagement/web/src/test/java/org/onosproject/mapping/web/MappingsWebResourceTest.java
@@ -30,7 +30,6 @@
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
 import org.onlab.packet.IpPrefix;
-import org.onlab.rest.BaseResource;
 import org.onosproject.codec.CodecService;
 import org.onosproject.codec.impl.CodecManager;
 import org.onosproject.mapping.DefaultMappingKey;
@@ -278,7 +277,7 @@
                         .add(DeviceService.class, mockDeviceService)
                         .add(CodecService.class, codecService);
 
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
     }
 
     /**
diff --git a/apps/netconf/client/pom.xml b/apps/netconf/client/pom.xml
index e37a3c5..eba0375 100644
--- a/apps/netconf/client/pom.xml
+++ b/apps/netconf/client/pom.xml
@@ -125,18 +125,6 @@
     </dependencies>
 
     <build>
-        <pluginManagement>
-            <plugins>
-
-                <plugin>
-                    <groupId>org.apache.karaf.tooling</groupId>
-                    <artifactId>karaf-maven-plugin</artifactId>
-                    <version>3.0.5</version>
-                    <extensions>true</extensions>
-                </plugin>
-
-            </plugins>
-        </pluginManagement>
 
         <plugins>
             <plugin>
diff --git a/apps/netconf/client/src/main/java/org/onosproject/netconf/client/EventData.java b/apps/netconf/client/src/main/java/org/onosproject/netconf/client/EventData.java
deleted file mode 100644
index 0dce327..0000000
--- a/apps/netconf/client/src/main/java/org/onosproject/netconf/client/EventData.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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.netconf.client;
-
-import org.onosproject.config.DynamicConfigEvent;
-import org.onosproject.net.DeviceId;
-import org.onosproject.yang.model.ResourceId;
-
-
-/**
- * Event details.
- */
-public class EventData {
-
-    private DeviceId devId;
-    private ResourceId key;
-    private DynamicConfigEvent.Type type;
-
-    /**
-     * Creates an instance of EventData.
-     *
-     * @param devId device id
-     * @param key device key
-     * @param type event type
-     */
-    public EventData(DeviceId devId, ResourceId key, DynamicConfigEvent.Type type) {
-        devId = devId;
-        key = key;
-        type = type;
-    }
-
-    public DeviceId getDevId() {
-        return devId;
-    }
-
-    public ResourceId getKey() {
-        return key;
-    }
-
-    public DynamicConfigEvent.Type getType() {
-        return type;
-    }
-}
\ No newline at end of file
diff --git a/apps/ofagent/src/test/java/org/onosproject/ofagent/rest/OFAgentWebResourceTest.java b/apps/ofagent/src/test/java/org/onosproject/ofagent/rest/OFAgentWebResourceTest.java
index 74e2c4a..32eb857 100644
--- a/apps/ofagent/src/test/java/org/onosproject/ofagent/rest/OFAgentWebResourceTest.java
+++ b/apps/ofagent/src/test/java/org/onosproject/ofagent/rest/OFAgentWebResourceTest.java
@@ -27,7 +27,6 @@
 import org.onlab.osgi.TestServiceDirectory;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.TpPort;
-import org.onlab.rest.BaseResource;
 import org.onosproject.incubator.net.virtual.NetworkId;
 import org.onosproject.incubator.net.virtual.TenantId;
 import org.onosproject.ofagent.api.OFAgent;
@@ -141,7 +140,7 @@
         ServiceDirectory testDirectory = new TestServiceDirectory()
                 .add(OFAgentAdminService.class, mockOFAgentAdminService)
                 .add(OFAgentService.class, mockOFAgentService);
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
     }
 
     /**
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/RulePopulatorUtil.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/RulePopulatorUtil.java
index 07a808d..5dbf7a7 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/RulePopulatorUtil.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/RulePopulatorUtil.java
@@ -98,6 +98,10 @@
             return null;
         }
 
+        if (device == null) {
+            return null;
+        }
+
         ExtensionTreatmentResolver resolver = device.as(ExtensionTreatmentResolver.class);
         ExtensionTreatment treatment = resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
         try {
diff --git a/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/impl/DefaultOpenstackNodeHandler.java b/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/impl/DefaultOpenstackNodeHandler.java
index e1da821..625d41d 100644
--- a/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/impl/DefaultOpenstackNodeHandler.java
+++ b/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/impl/DefaultOpenstackNodeHandler.java
@@ -474,6 +474,10 @@
             return null;
         }
 
+        if (device == null) {
+            return null;
+        }
+
         ExtensionTreatmentResolver resolver = device.as(ExtensionTreatmentResolver.class);
         ExtensionTreatment treatment = resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
         try {
diff --git a/apps/optical-model/src/main/java/org/onosproject/net/optical/config/OpticalPortOperator.java b/apps/optical-model/src/main/java/org/onosproject/net/optical/config/OpticalPortOperator.java
index f9ee752..ee5ac99 100644
--- a/apps/optical-model/src/main/java/org/onosproject/net/optical/config/OpticalPortOperator.java
+++ b/apps/optical-model/src/main/java/org/onosproject/net/optical/config/OpticalPortOperator.java
@@ -93,7 +93,7 @@
         }
 
         OpticalPortConfig opc = lookupConfig(cp);
-        if (opc == null) {
+        if (descr == null || opc == null) {
             return descr;
         }
 
diff --git a/apps/pce/app/src/main/java/org/onosproject/pce/pceservice/PceManager.java b/apps/pce/app/src/main/java/org/onosproject/pce/pceservice/PceManager.java
index 47c369c..d1e3d66 100644
--- a/apps/pce/app/src/main/java/org/onosproject/pce/pceservice/PceManager.java
+++ b/apps/pce/app/src/main/java/org/onosproject/pce/pceservice/PceManager.java
@@ -398,7 +398,7 @@
                     //Log.info("computeExplicitPath :: finalComputedPath " + finalComputedPath);
 
                     if (finalComputedPath != null && !finalComputedPath.get(finalComputedPath.size() - 1).links()
-                            .contains((Link) info.value())) {
+                            .contains(info.value())) {
                         finalComputedPath = null;
                     }
                 }
@@ -610,10 +610,7 @@
             return false;
         }
 
-        DisjointPath path = null;
-        if (!paths.isEmpty()) {
-            path = paths.iterator().next();
-        }
+        DisjointPath path = paths.iterator().next();
 
         Builder annotationBuilder = DefaultAnnotations.builder();
         double bw = 0;
@@ -916,7 +913,6 @@
     private boolean releaseSharedBwForNewTunnel(Path computedPath, double bandwidthConstraint,
                                                 SharedBandwidthConstraint shBwConstraint) {
         checkNotNull(computedPath);
-        checkNotNull(bandwidthConstraint);
         double bwToAllocate;
 
         Double additionalBwValue = null;
@@ -1074,7 +1070,6 @@
     private boolean reserveBandwidth(Path computedPath, double bandwidthConstraint,
                                   SharedBandwidthConstraint shBwConstraint) {
         checkNotNull(computedPath);
-        checkNotNull(bandwidthConstraint);
         Resource resource = null;
         double bwToAllocate = 0;
         Map<Link, Double> linkMap = new HashMap<>();
diff --git a/apps/pce/app/src/test/java/org/onosproject/pce/pceservice/DefaultPcePathTest.java b/apps/pce/app/src/test/java/org/onosproject/pce/pceservice/DefaultPcePathTest.java
index 4b9d0b9..9d95e44 100644
--- a/apps/pce/app/src/test/java/org/onosproject/pce/pceservice/DefaultPcePathTest.java
+++ b/apps/pce/app/src/test/java/org/onosproject/pce/pceservice/DefaultPcePathTest.java
@@ -17,21 +17,8 @@
 package org.onosproject.pce.pceservice;
 
 import com.google.common.collect.Lists;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-import static org.onosproject.pce.pceservice.PathComputationTest.D2;
-import static org.easymock.EasyMock.createMock;
-
 import com.google.common.testing.EqualsTester;
-
-import org.onlab.osgi.ServiceDirectory;
-import org.onlab.osgi.TestServiceDirectory;
-import org.onlab.rest.BaseResource;
+import org.junit.Test;
 import org.onosproject.incubator.net.tunnel.TunnelId;
 import org.onosproject.pce.pceservice.constraint.CostConstraint;
 import org.onosproject.pce.pceservice.constraint.PceBandwidthConstraint;
@@ -39,23 +26,17 @@
 
 import java.util.List;
 
+import static org.easymock.EasyMock.createMock;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.onosproject.pce.pceservice.PathComputationTest.D2;
+
 /**
  * Unit tests for DefaultPcePath class.
  */
 public class DefaultPcePathTest {
     private PceStore pceStore = createMock(PceStore.class);
 
-    @Before
-    public void setup() {
-
-       ServiceDirectory testDirectory = new TestServiceDirectory()
-                   .add(PceStore.class, pceStore);
-       BaseResource.setServiceDirectory(testDirectory);
-    }
-
-    @After
-    public void tearDownTest() {
-    }
     /**
      * Checks the operation of equals() methods.
      */
diff --git a/apps/pce/pcerest/src/test/java/org/onosproject/pcerest/PcePathResourceTest.java b/apps/pce/pcerest/src/test/java/org/onosproject/pcerest/PcePathResourceTest.java
index ae05d7e..51f5c2d 100644
--- a/apps/pce/pcerest/src/test/java/org/onosproject/pcerest/PcePathResourceTest.java
+++ b/apps/pce/pcerest/src/test/java/org/onosproject/pcerest/PcePathResourceTest.java
@@ -44,6 +44,7 @@
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.onlab.junit.TestUtils;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
 import org.onlab.packet.IpAddress;
@@ -109,7 +110,7 @@
                                                                   .add(TunnelService.class, tunnelService)
                                                                   .add(PceStore.class, pceStore)
                                                                   .add(CodecService.class, context.codecManager());
-       BaseResource.setServiceDirectory(testDirectory);
+       TestUtils.setField(BaseResource.class, "services", testDirectory);
 
        // Tunnel creation
        // Links
diff --git a/apps/pce/pceweb/src/main/java/org/onosproject/pceweb/PceWebTopovMessageHandler.java b/apps/pce/pceweb/src/main/java/org/onosproject/pceweb/PceWebTopovMessageHandler.java
index a1a9a25..4306f61 100644
--- a/apps/pce/pceweb/src/main/java/org/onosproject/pceweb/PceWebTopovMessageHandler.java
+++ b/apps/pce/pceweb/src/main/java/org/onosproject/pceweb/PceWebTopovMessageHandler.java
@@ -772,7 +772,7 @@
     private void findTunnelAndHighlights() {
         Collection<Tunnel> tunnelSet = null;
         Highlights highlights = new Highlights();
-        paths.removeAll(paths);
+        paths.clear();
         tunnelSet = tunnelService.queryTunnel(MPLS);
         if (tunnelSet.size() == 0) {
             log.warn("Tunnel does not exist");
@@ -819,7 +819,7 @@
      */
     private void highlightsForTunnel(List<Tunnel> tunnels) {
         Highlights highlights = new Highlights();
-        paths.removeAll(paths);
+        paths.clear();
 
         if (tunnels.isEmpty()) {
             log.error("path does not exist");
diff --git a/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/SdnIpReactiveRouting.java b/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/SdnIpReactiveRouting.java
index 5b6ad9c..906e34d 100644
--- a/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/SdnIpReactiveRouting.java
+++ b/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/SdnIpReactiveRouting.java
@@ -263,8 +263,10 @@
         case HOST_TO_INTERNET:
             // If the destination IP address is outside the local SDN network.
             // The Step 1 has already handled it. We do not need to do anything here.
-            intentRequestListener.setUpConnectivityHostToInternet(srcIpAddress,
-                    ipPrefix, route.nextHop());
+            if (route != null) {
+                intentRequestListener.setUpConnectivityHostToInternet(srcIpAddress,
+                        ipPrefix, route.nextHop());
+            }
             break;
         case INTERNET_TO_HOST:
             intentRequestListener.setUpConnectivityInternetToHost(dstIpAddress);
diff --git a/apps/restconf/api/BUCK b/apps/restconf/api/BUCK
index b54f53c..39a3872 100644
--- a/apps/restconf/api/BUCK
+++ b/apps/restconf/api/BUCK
@@ -6,6 +6,11 @@
     '//lib:jersey-server',
 ]
 
+TEST_DEPS = [
+    '//lib:TEST_REST',
+]
+
 osgi_jar_with_tests (
     deps = COMPILE_DEPS,
+    test_deps = TEST_DEPS,
 )
diff --git a/apps/restconf/api/pom.xml b/apps/restconf/api/pom.xml
index cadd950..9bc8fea 100644
--- a/apps/restconf/api/pom.xml
+++ b/apps/restconf/api/pom.xml
@@ -47,6 +47,11 @@
             <groupId>org.glassfish.jersey.containers</groupId>
             <artifactId>jersey-container-servlet</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-junit</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
 </project>
diff --git a/apps/restconf/api/src/main/java/org/onosproject/restconf/api/RestconfError.java b/apps/restconf/api/src/main/java/org/onosproject/restconf/api/RestconfError.java
new file mode 100644
index 0000000..61c7144
--- /dev/null
+++ b/apps/restconf/api/src/main/java/org/onosproject/restconf/api/RestconfError.java
@@ -0,0 +1,332 @@
+/*
+ * 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.restconf.api;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import java.util.List;
+
+/**
+ * Restconf error structured as in "ietf-restconf@2017-01-26.yang".
+ * An entry containing information about one specific error that occurred while
+ * processing a RESTCONF request.
+ */
+public final class RestconfError {
+    private final ErrorType errorType;
+    private final ErrorTag errorTag;
+    private String errorAppTag;
+    private String errorPath;
+    private String errorMessage;
+    private String errorInfo;
+
+    private RestconfError(ErrorType errorType, ErrorTag errorTag) {
+        this.errorType = errorType;
+        this.errorTag = errorTag;
+    }
+
+    /**
+     * The protocol layer where the error occurred.
+     * @return The error type
+     */
+    public ErrorType getErrorType() {
+        return errorType;
+    }
+
+    /**
+     * The enumerated error-tag.
+     * @return The enumerated error-tag
+     */
+    public ErrorTag getErrorTag() {
+        return errorTag;
+    }
+
+    /**
+     * The application-specific error-tag.
+     * @return The application-specific error-tag
+     */
+    public String getErrorAppTag() {
+        return errorAppTag;
+    }
+
+    /**
+     * The YANG instance identifier associated with the error node.
+     * @return A yang instance represented in XPath
+     */
+    public String getErrorPath() {
+        return errorPath;
+    }
+
+    /**
+     * A message describing the error.
+     * @return A message describing the error
+     */
+    public String getErrorMessage() {
+        return errorMessage;
+    }
+
+    /**
+     * A container with zero or more data nodes representing additional error information.
+     * @return A string serialization of datanodes e.g. a stack trace
+     */
+    public String getErrorInfo() {
+        return errorInfo;
+    }
+
+    /**
+     * Convert the restconf error to Json - this is for one error - many may be included in a response.
+     * @return A JSON object containing the details of one error
+     */
+    public ObjectNode toJson() {
+        ObjectMapper mapper = new ObjectMapper();
+        ObjectNode errorTags = (ObjectNode) mapper.createObjectNode();
+        errorTags.put("error-type", errorType.name().toLowerCase());
+        errorTags.put("error-tag", errorTag.text());
+
+        if (errorAppTag != null) {
+            errorTags.put("error-app-tag", errorAppTag);
+        }
+
+        if (errorPath != null) {
+            errorTags.put("error-path", errorPath);
+        }
+
+        if (errorMessage != null) {
+            errorTags.put("error-message", errorMessage);
+        }
+
+        if (errorInfo != null) {
+            errorTags.put("error-info", errorInfo);
+        }
+
+        ObjectNode errorNode = (ObjectNode) mapper.createObjectNode();
+        errorNode.put("error", errorTags);
+        return errorNode;
+    }
+
+    /**
+     * An enumerated set of the protocol layer involved in a RESTCONF request.
+     */
+    public enum ErrorType {
+        //The transport layer
+        TRANSPORT,
+        //The rpc or notification layer
+        RPC,
+        //The protocol operation layer
+        PROTOCOL,
+        //The server application layer
+        APPLICATION
+    }
+
+    /**
+     * An enumerated set of error 'tags' - consistent labels for error causes.
+     * See <a href="https://tools.ietf.org/html/rfc8040#section-7">Section 7 of RFC 8040</a>
+     * for a list of HTTP status codes associated with these
+     * error tags. Developers should ensure that suitable HTTP status codes are used
+     * when raising RESTCONF errors
+     */
+    public enum ErrorTag {
+        /**
+         * Use with Response 409 {@link javax.ws.rs.core.Response.Status#CONFLICT}.
+         */
+        IN_USE("in-use"),
+        /**
+         * Use with Response 400 {@link javax.ws.rs.core.Response.Status#BAD_REQUEST},
+         * 404 {@link javax.ws.rs.core.Response.Status#NOT_FOUND} or
+         * 406 {@link javax.ws.rs.core.Response.Status#NOT_ACCEPTABLE}.
+         */
+        INVALID_VALUE("invalid-value"),
+        /**
+         * Use with Response 413 {@link javax.ws.rs.core.Response.Status#REQUEST_ENTITY_TOO_LARGE}.
+         */
+        REQUEST_TOO_BIG("too-big"),
+        /**
+         * Use with Response 400 {@link javax.ws.rs.core.Response.Status#BAD_REQUEST}.
+         */
+        RESPONSE_TOO_BIG("too-big"),
+        /**
+         * Use with Response 400 {@link javax.ws.rs.core.Response.Status#BAD_REQUEST}.
+         */
+        MISSING_ATTRIBUTE("missing-attribute"),
+        /**
+         * Use with Response 400 {@link javax.ws.rs.core.Response.Status#BAD_REQUEST}.
+         */
+        BAD_ATTRIBUTE("bad-attribute"),
+        /**
+         * Use with Response 400 {@link javax.ws.rs.core.Response.Status#BAD_REQUEST}.
+         */
+        UNKNOWN_ATTRIBUTE("unknown-attribute"),
+        /**
+         * Use with Response 400 {@link javax.ws.rs.core.Response.Status#BAD_REQUEST}.
+         */
+        BAD_ELEMENT("bad-element"),
+        /**
+         * Use with Response 400 {@link javax.ws.rs.core.Response.Status#BAD_REQUEST}.
+         */
+        UNKNOWN_ELEMENT("unknown-element"),
+        /**
+         * Use with Response 400 {@link javax.ws.rs.core.Response.Status#BAD_REQUEST}.
+         */
+        UNKNOWN_NAMESPACE("unknown-namespace"),
+        /**
+         * Use with Response 401 {@link javax.ws.rs.core.Response.Status#UNAUTHORIZED},
+         * or 403 {@link javax.ws.rs.core.Response.Status#FORBIDDEN}.
+         */
+        ACCESS_DENIED("access-denied"),
+        /**
+         * Use with Response 409 {@link javax.ws.rs.core.Response.Status#CONFLICT}.
+         */
+        LOCK_DENIED("lock-denied"),
+        /**
+         * Use with Response 409 {@link javax.ws.rs.core.Response.Status#CONFLICT}.
+         */
+        RESOURCE_DENIED("resource-denied"),
+        /**
+         * Use with Response 500 {@link javax.ws.rs.core.Response.Status#INTERNAL_SERVER_ERROR}.
+         */
+        ROLLBACK_FAILED("rollback-failed"),
+        /**
+         * Use with Response 409 {@link javax.ws.rs.core.Response.Status#CONFLICT}.
+         */
+        DATA_EXISTS("data-exists"),
+        /**
+         * Use with Response 409 {@link javax.ws.rs.core.Response.Status#CONFLICT}.
+         */
+        DATA_MISSING("data-missing"),
+        /**
+         * Use with Response 405 {@link javax.ws.rs.core.Response.Status#METHOD_NOT_ALLOWED},
+         * or 501 {@link javax.ws.rs.core.Response.Status#NOT_IMPLEMENTED}.
+         */
+        OPERATION_NOT_SUPPORTED("operation-not-supported"),
+        /**
+         * Use with Response 412 {@link javax.ws.rs.core.Response.Status#PRECONDITION_FAILED},
+         * or 500 {@link javax.ws.rs.core.Response.Status#INTERNAL_SERVER_ERROR}.
+         */
+        OPERATION_FAILED("operation-failed"),
+        /**
+         * Use with Response 500 {@link javax.ws.rs.core.Response.Status#INTERNAL_SERVER_ERROR}.
+         */
+        PARTIAL_OPERATION("partial-operation"),
+        /**
+         * Use with Response 400 {@link javax.ws.rs.core.Response.Status#BAD_REQUEST}.
+         */
+        MALFORMED_MESSAGE("malformed-message");
+
+        private String text;
+
+        ErrorTag(String text) {
+            this.text = text;
+        }
+
+        /**
+         * Lowercase version of the error tag compliant with the standard.
+         * @return the associated lowercase version
+         */
+        public String text() {
+            return text;
+        }
+    }
+
+    /**
+     * Build a new RestconfError. ErrorTag and ErrorType are mandatory parameters.
+     * @param errorType The error type
+     * @param errorTag The error Tag
+     * @return A build which an be used to create the RestconfError
+     */
+    public static RestconfError.Builder builder(ErrorType errorType, ErrorTag errorTag) {
+        return new Builder(errorType, errorTag);
+    }
+
+    /**
+     * Create the complete JSON representation of the errors for the Response.
+     * Note this can contain many individual RestconfErrors
+     * @param restconfErrors A list of {@link RestconfError}s
+     * @return A JSON node which can be used in a response
+     */
+    public static ObjectNode wrapErrorAsJson(List<RestconfError> restconfErrors) {
+        ObjectMapper mapper = new ObjectMapper();
+        ArrayNode errorArray = mapper.createArrayNode();
+        restconfErrors.forEach(error -> errorArray.add(error.toJson()));
+        ObjectNode errorsNode = (ObjectNode) mapper.createObjectNode();
+        errorsNode.put("ietf-restconf:errors", errorArray);
+        return errorsNode;
+    }
+
+    /**
+     * A builder for the Restconf Error. ErrorTag and ErrorType are mandatory parameters.
+     */
+    public static final class Builder {
+        private RestconfError restconfError;
+
+        /**
+         * ErrorTag and ErrorType are mandatory parameters of the error.
+         * @param errorType The error-type
+         * @param errorTag The error-tag
+         */
+        private Builder(ErrorType errorType, ErrorTag errorTag) {
+            restconfError = new RestconfError(errorType, errorTag);
+        }
+
+        /**
+         * Set an error-app-tag on the error.
+         * @param errorAppTag a tag relevant to the error in the application
+         * @return The builder
+         */
+        public Builder errorAppTag(String errorAppTag) {
+            this.restconfError.errorAppTag = errorAppTag;
+            return this;
+        }
+
+        /**
+         * Set an error-path on the error.
+         * @param errorPath A path to the resource that caused the error
+         * @return The builder
+         */
+        public Builder errorPath(String errorPath) {
+            this.restconfError.errorPath = errorPath;
+            return this;
+        }
+
+        /**
+         * Set an error-message on the error.
+         * @param errorMessage an explaination of the error
+         * @return The builder
+         */
+        public Builder errorMessage(String errorMessage) {
+            this.restconfError.errorMessage = errorMessage;
+            return this;
+        }
+
+        /**
+         * Set an error-info on the error.
+         * @param errorInfo Any additional infor about the error
+         * @return The builder
+         */
+        public Builder errorInfo(String errorInfo) {
+            this.restconfError.errorInfo = errorInfo;
+            return this;
+        }
+
+        /**
+         * Build the contents of the builder in to a {@link RestconfError}.
+         * @return A RestconfError
+         */
+        public RestconfError build() {
+            return restconfError;
+        }
+    }
+}
diff --git a/apps/restconf/api/src/main/java/org/onosproject/restconf/api/RestconfException.java b/apps/restconf/api/src/main/java/org/onosproject/restconf/api/RestconfException.java
index 8f12b7f..459531f 100644
--- a/apps/restconf/api/src/main/java/org/onosproject/restconf/api/RestconfException.java
+++ b/apps/restconf/api/src/main/java/org/onosproject/restconf/api/RestconfException.java
@@ -15,53 +15,123 @@
  */
 package org.onosproject.restconf.api;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
 import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.core.Response;
 
+import java.util.ArrayList;
+import java.util.Optional;
+
 import static javax.ws.rs.core.Response.Status;
 
 /**
- * Exceptions raised during RESTCONF operations. This class extends
- * WebApplicationException. The design intention is to create a place holder
- * for RESTCONF specific errors and to be able to add more functions as the
- * subsystem grows.
+ * Exceptions raised during RESTCONF operations. This class extends WebApplicationException.
+ * To comply with the RESTCONF specification on error handling, the parameters given
+ * when creating this exception will be used to create one
+ * {@link org.onosproject.restconf.api.RestconfError}. Additional
+ * {@link org.onosproject.restconf.api.RestconfError}s may be added subsequently.
+ * This exception should be converted to a Json object using the toRestconfError()
+ * method before being passed back to the caller in a response
  */
 public class RestconfException extends WebApplicationException {
 
     // This is a randomly generated value. A WebApplicationException class is required to define it.
-    private static final long SERIAL_VERSION_UID = 3275970397584007046L;
+    private static final long SERIAL_VERSION_UID = 1275970654684007046L;
+
+    private ArrayList<RestconfError> restconfErrors = new ArrayList<>();
 
     /**
-     * Constructs a new RESTCONF server error exception. The caller raising this
-     * exception may pass in a HTTP error status code and an error message. The
-     * error code will be displayed to the RESTCONF client as part of the
-     * response from the RESTCONF server. The error message is a string which
-     * may be saved in a log file and may be later retrieved by the
-     * getMessage() method.
+     * Constructs a new RESTCONF server error exception based of an existing exception.
+     * The caller raising this exception may pass in a HTTP error status code
+     * and an error message. The error code will be displayed to the RESTCONF
+     * client as part of the response from the RESTCONF server. The error message
+     * is a string which may be saved in a log file and may be later retrieved by
+     * the getMessage() method. The parameters given will be formed in to a
+     * {@link org.onosproject.restconf.api.RestconfError}. Additional errors may be
+     * added after construction.
      *
      * @param message the detailed error message
-     * @param status  HTTP error status
+     * @param throwable The existing exception that caused this response.
+     *                  The message from this exception will be used as error-info
+     * @param errorTag A Restconf Error tag
+     * @param status  HTTP error status. Developers are asked to ensure that the correct one for the error-tag is used
+     * @param errorPath An optional path that gives the item that caused the exception
      * @throws IllegalArgumentException in case the status code is null or is not from
      *                                  javax.ws.rs.core.Response.Status.Family
      *                                  status code family
      */
-    public RestconfException(String message, Status status) {
-        super(message, null, Response.status(status).build());
+    public RestconfException(String message, Throwable throwable,
+                             RestconfError.ErrorTag errorTag, Status status,
+                             Optional<String> errorPath) {
+        super(message, throwable, Response.status(status).build());
+        RestconfError.Builder bldr = RestconfError
+                        .builder(RestconfError.ErrorType.APPLICATION, errorTag)
+                        .errorMessage(message);
+        if (throwable != null) {
+            bldr.errorInfo(throwable.getMessage());
+        }
+        if (errorPath.isPresent()) {
+            bldr.errorPath(errorPath.get());
+        }
+        addToErrors(bldr.build());
     }
 
     /**
-     * Constructs a new RESTCONF server error exception. The caller raising
-     * this exception may pass in the numerical value of a HTTP error
-     * status code, The error code will be displayed to the RESTCONF client
-     * as a response from the RESTCONF server.
+     * Constructs a new RESTCONF server error exception.
+     * The caller raising this exception may pass in a HTTP error status code
+     * and an error message. The error code will be displayed to the RESTCONF
+     * client as part of the response from the RESTCONF server. The error message
+     * is a string which may be saved in a log file and may be later retrieved by
+     * the getMessage() method. The parameters given will be formed in to a
+     * {@link org.onosproject.restconf.api.RestconfError}. Additional errors may be
+     * added after construction.
      *
-     * @param status HTTP error status
-     * @throws IllegalArgumentException in case the status code is not a valid
-     *                                  HTTP status code or if it is not from the
+     * @param message the detailed error message
+     * @param errorTag A Restconf Error tag
+     * @param status  HTTP error status. Developers are asked to ensure that the correct one for the error-tag is used
+     * @param errorPath An optional path that gives the item that caused the exception
+     * @param errorInfo An optional string with more info about the error
+     * @throws IllegalArgumentException in case the status code is null or is not from
      *                                  javax.ws.rs.core.Response.Status.Family
      *                                  status code family
      */
-    public RestconfException(int status) {
-        super((Throwable) null, Response.status(status).build());
+    public RestconfException(String message, RestconfError.ErrorTag errorTag,
+                             Status status, Optional<String> errorPath,
+                             Optional<String> errorInfo) {
+        super(message, null, Response.status(status).build());
+        RestconfError.Builder bldr = RestconfError
+                .builder(RestconfError.ErrorType.APPLICATION, errorTag)
+                .errorMessage(message);
+        if (errorInfo.isPresent()) {
+            bldr.errorInfo(errorInfo.get());
+        }
+        if (errorPath.isPresent()) {
+            bldr.errorPath(errorPath.get());
+        }
+        addToErrors(bldr.build());
+    }
+
+    /**
+     * Allows additional RestconfErrors to be added to the exception.
+     * @param restconfError An additional RestconfError to be added to the response
+     */
+    public void addToErrors(RestconfError restconfError) {
+        restconfErrors.add(restconfError);
+    }
+
+    /**
+     * Convert the RestconfException and all of its RestconfErrors in to a Json object.
+     * @return A json node generated from the RestconfException
+     */
+    public ObjectNode toRestconfErrorJson() {
+        ObjectMapper mapper = new ObjectMapper();
+        ArrayNode errorArray = mapper.createArrayNode();
+        restconfErrors.forEach(error -> errorArray.add(error.toJson()));
+        ObjectNode errorsNode = (ObjectNode) mapper.createObjectNode();
+        errorsNode.put("ietf-restconf:errors", errorArray);
+        return errorsNode;
     }
 }
diff --git a/apps/restconf/api/src/main/java/org/onosproject/restconf/api/RestconfService.java b/apps/restconf/api/src/main/java/org/onosproject/restconf/api/RestconfService.java
index 9f3ba53..9a12bd6 100644
--- a/apps/restconf/api/src/main/java/org/onosproject/restconf/api/RestconfService.java
+++ b/apps/restconf/api/src/main/java/org/onosproject/restconf/api/RestconfService.java
@@ -23,7 +23,7 @@
 
 /**
  * Abstraction of RESTCONF Server functionality according to the
- * RESTCONF RFC (no official RFC number yet).
+ * RESTCONF RFC 8040.
  */
 public interface RestconfService {
     /**
diff --git a/apps/restconf/api/src/test/java/org/onosproject/restconf/api/RestconfExceptionTest.java b/apps/restconf/api/src/test/java/org/onosproject/restconf/api/RestconfExceptionTest.java
new file mode 100644
index 0000000..c9a1d04
--- /dev/null
+++ b/apps/restconf/api/src/test/java/org/onosproject/restconf/api/RestconfExceptionTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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.restconf.api;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.junit.Test;
+
+import javax.ws.rs.core.Response;
+
+import java.util.Arrays;
+import java.util.Optional;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test the conversion of RestconfErrors in to JSON.
+ */
+public class RestconfExceptionTest {
+
+    private RestconfError error1 = RestconfError
+            .builder(RestconfError.ErrorType.TRANSPORT, RestconfError.ErrorTag.ACCESS_DENIED)
+            .build();
+
+    private RestconfError error2 = RestconfError
+            .builder(RestconfError.ErrorType.TRANSPORT, RestconfError.ErrorTag.BAD_ATTRIBUTE)
+            .build();
+
+    private RestconfError error3 = RestconfError
+            .builder(RestconfError.ErrorType.RPC, RestconfError.ErrorTag.BAD_ELEMENT)
+            .errorAppTag("my-app-tag")
+            .errorMessage("a message about the error")
+            .errorPath("/a/b/c")
+            .errorInfo("info about the error")
+            .build();
+
+    /**
+     * Test a Restconf Exception with many RestconfErrors converted to Json.
+     */
+    @Test
+    public void testToRestconfErrorJson() {
+        IllegalArgumentException ie = new IllegalArgumentException("This is a test");
+        RestconfException e = new RestconfException("Error in system", ie,
+                RestconfError.ErrorTag.DATA_EXISTS, Response.Status.BAD_REQUEST,
+                Optional.of("/some/path"));
+        e.addToErrors(error1);
+        e.addToErrors(error2);
+        e.addToErrors(error3);
+
+        assertEquals("{\"ietf-restconf:errors\":[" +
+                "{\"error\":{" +
+                    "\"error-type\":\"application\"," +
+                    "\"error-tag\":\"data-exists\"," +
+                    "\"error-path\":\"/some/path\"," +
+                    "\"error-message\":\"Error in system\"," +
+                    "\"error-info\":\"This is a test\"}}," +
+                "{\"error\":{" +
+                    "\"error-type\":\"transport\"," +
+                    "\"error-tag\":\"access-denied\"}}," +
+                "{\"error\":{" +
+                    "\"error-type\":\"transport\"," +
+                    "\"error-tag\":\"bad-attribute\"}}," +
+                "{\"error\":{" +
+                    "\"error-type\":\"rpc\"," +
+                    "\"error-tag\":\"bad-element\"," +
+                    "\"error-app-tag\":\"my-app-tag\"," +
+                    "\"error-path\":\"/a/b/c\"," +
+                    "\"error-message\":\"a message about the error\"," +
+                    "\"error-info\":\"info about the error\"}}]}",
+                e.toRestconfErrorJson().toString());
+    }
+
+    @Test
+    public void testWrappingErrorsNotInException() {
+        RestconfError error4 = RestconfError
+                .builder(RestconfError.ErrorType.TRANSPORT, RestconfError.ErrorTag.UNKNOWN_ELEMENT)
+                .build();
+
+        ObjectNode json = RestconfError.wrapErrorAsJson(Arrays.asList(error4, error1, error2, error3));
+
+        assertEquals("{\"ietf-restconf:errors\":[" +
+                    "{\"error\":{" +
+                        "\"error-type\":\"transport\"," +
+                        "\"error-tag\":\"unknown-element\"}}," +
+                    "{\"error\":{" +
+                        "\"error-type\":\"transport\"," +
+                        "\"error-tag\":\"access-denied\"}}," +
+                    "{\"error\":{" +
+                        "\"error-type\":\"transport\"," +
+                        "\"error-tag\":\"bad-attribute\"}}," +
+                    "{\"error\":{" +
+                        "\"error-type\":\"rpc\"," +
+                        "\"error-tag\":\"bad-element\"," +
+                        "\"error-app-tag\":\"my-app-tag\"," +
+                        "\"error-path\":\"/a/b/c\"," +
+                        "\"error-message\":\"a message about the error\"," +
+                        "\"error-info\":\"info about the error\"}}]}",
+                json.toString());
+
+    }
+}
diff --git a/apps/restconf/restconfmgr/src/main/java/org/onosproject/restconf/restconfmanager/RestconfManager.java b/apps/restconf/restconfmgr/src/main/java/org/onosproject/restconf/restconfmanager/RestconfManager.java
index 5334cd0..0ad6b25 100644
--- a/apps/restconf/restconfmgr/src/main/java/org/onosproject/restconf/restconfmanager/RestconfManager.java
+++ b/apps/restconf/restconfmgr/src/main/java/org/onosproject/restconf/restconfmanager/RestconfManager.java
@@ -28,6 +28,7 @@
 import org.onosproject.config.DynamicConfigService;
 import org.onosproject.config.FailedException;
 import org.onosproject.config.Filter;
+import org.onosproject.restconf.api.RestconfError;
 import org.onosproject.restconf.api.RestconfException;
 import org.onosproject.restconf.api.RestconfRpcOutput;
 import org.onosproject.restconf.api.RestconfService;
@@ -46,13 +47,17 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.ws.rs.core.Response;
 import java.net.URI;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
+import static javax.ws.rs.core.Response.Status.CONFLICT;
 import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
 import static org.onosproject.d.config.ResourceIds.parentOf;
 import static org.onosproject.restconf.utils.RestconfUtils.convertDataNodeToJson;
@@ -119,8 +124,9 @@
             dataNode = dynamicConfigService.readNode(rl.ridForDynConfig(), filter);
         } catch (FailedException e) {
             log.error("ERROR: DynamicConfigService: ", e);
-            throw new RestconfException("ERROR: DynamicConfigService",
-                                        INTERNAL_SERVER_ERROR);
+            throw new RestconfException("ERROR: DynamicConfigService", e,
+                    RestconfError.ErrorTag.OPERATION_FAILED, INTERNAL_SERVER_ERROR,
+                    Optional.of(uri.getPath()));
         }
         ObjectNode rootNode = convertDataNodeToJson(rl.ridForYangRuntime(), dataNode);
         return rootNode;
@@ -146,9 +152,16 @@
         try {
             dynamicConfigService.createNode(rl.ridForDynConfig(), dataNode);
         } catch (FailedException e) {
-            log.error("ERROR: DynamicConfigService: ", e);
-            throw new RestconfException("ERROR: DynamicConfigService",
-                                        INTERNAL_SERVER_ERROR);
+            if (e.getMessage().startsWith("Requested node already present")) {
+                throw new RestconfException("Already exists", e,
+                        RestconfError.ErrorTag.DATA_EXISTS, CONFLICT,
+                        Optional.of(uri.getPath()));
+            } else {
+                log.error("ERROR: DynamicConfigService: ", e);
+                throw new RestconfException("ERROR: DynamicConfigService", e,
+                    RestconfError.ErrorTag.OPERATION_FAILED, INTERNAL_SERVER_ERROR,
+                    Optional.of(uri.getPath()));
+            }
         }
     }
 
@@ -176,8 +189,9 @@
 
         } catch (FailedException e) {
             log.error("ERROR: DynamicConfigService: ", e);
-            throw new RestconfException("ERROR: DynamicConfigService",
-                                        INTERNAL_SERVER_ERROR);
+            throw new RestconfException("ERROR: DynamicConfigService", e,
+                RestconfError.ErrorTag.OPERATION_FAILED, INTERNAL_SERVER_ERROR,
+                Optional.of(uri.getPath()));
         }
     }
 
@@ -191,8 +205,9 @@
             }
         } catch (FailedException e) {
             log.error("ERROR: DynamicConfigService: ", e);
-            throw new RestconfException("ERROR: DynamicConfigService",
-                                        INTERNAL_SERVER_ERROR);
+            throw new RestconfException("ERROR: DynamicConfigService", e,
+                RestconfError.ErrorTag.OPERATION_FAILED, INTERNAL_SERVER_ERROR,
+                Optional.of(uri.getPath()));
         }
     }
 
@@ -217,8 +232,9 @@
             dynamicConfigService.updateNode(parentOf(rl.ridForDynConfig()), dataNode);
         } catch (FailedException e) {
             log.error("ERROR: DynamicConfigService: ", e);
-            throw new RestconfException("ERROR: DynamicConfigService",
-                                        INTERNAL_SERVER_ERROR);
+            throw new RestconfException("ERROR: DynamicConfigService", e,
+                RestconfError.ErrorTag.OPERATION_FAILED, INTERNAL_SERVER_ERROR,
+                Optional.of(uri.getPath()));
         }
     }
 
@@ -240,6 +256,10 @@
                                      ChunkedOutput<String> output)
             throws RestconfException {
         //TODO: to be completed
+        throw new RestconfException("Not implemented",
+                RestconfError.ErrorTag.OPERATION_NOT_SUPPORTED,
+                Response.Status.NOT_IMPLEMENTED,
+                Optional.empty(), Optional.of("subscribeEventStream not yet implemented"));
     }
 
     @Override
@@ -267,12 +287,26 @@
         } catch (InterruptedException e) {
             log.error("ERROR: computeResultQ.take() has been interrupted.");
             log.debug("executeRpc Exception:", e);
-            restconfOutput = new RestconfRpcOutput(INTERNAL_SERVER_ERROR, null);
+            RestconfError error =
+                    RestconfError.builder(RestconfError.ErrorType.RPC,
+                    RestconfError.ErrorTag.OPERATION_FAILED)
+                        .errorMessage("RPC execution has been interrupted")
+                        .errorPath(uri.getPath())
+                        .build();
+            restconfOutput = new RestconfRpcOutput(INTERNAL_SERVER_ERROR,
+                    RestconfError.wrapErrorAsJson(Arrays.asList(error)));
             restconfOutput.reason("RPC execution has been interrupted");
         } catch (Exception e) {
             log.error("ERROR: executeRpc: {}", e.getMessage());
             log.debug("executeRpc Exception:", e);
-            restconfOutput = new RestconfRpcOutput(INTERNAL_SERVER_ERROR, null);
+            RestconfError error =
+                    RestconfError.builder(RestconfError.ErrorType.RPC,
+                            RestconfError.ErrorTag.OPERATION_FAILED)
+                            .errorMessage(e.getMessage())
+                            .errorPath(uri.getPath())
+                            .build();
+            restconfOutput = new RestconfRpcOutput(INTERNAL_SERVER_ERROR,
+                    RestconfError.wrapErrorAsJson(Arrays.asList(error)));
             restconfOutput.reason(e.getMessage());
         }
 
@@ -312,7 +346,8 @@
             }
             parentId = rid.copyBuilder().removeLastKey().build();
         } catch (CloneNotSupportedException e) {
-            e.printStackTrace();
+            log.error("getDataForStore()", e);
+            return null;
         }
         ResourceData.Builder resData = DefaultResourceData.builder();
         resData.addDataNode(dbr.build());
diff --git a/apps/restconf/utils/src/main/java/org/onosproject/restconf/utils/RestconfUtils.java b/apps/restconf/utils/src/main/java/org/onosproject/restconf/utils/RestconfUtils.java
index cd0b428..41fcf39 100644
--- a/apps/restconf/utils/src/main/java/org/onosproject/restconf/utils/RestconfUtils.java
+++ b/apps/restconf/utils/src/main/java/org/onosproject/restconf/utils/RestconfUtils.java
@@ -20,9 +20,9 @@
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.apache.commons.io.IOUtils;
 import org.onlab.osgi.DefaultServiceDirectory;
+import org.onosproject.restconf.api.RestconfError;
 import org.onosproject.restconf.api.RestconfException;
 import org.onosproject.restconf.api.RestconfRpcOutput;
-import org.onosproject.restconf.utils.exceptions.RestconfUtilsException;
 import org.onosproject.yang.model.DataNode;
 import org.onosproject.yang.model.DefaultResourceData;
 import org.onosproject.yang.model.ResourceData;
@@ -43,13 +43,9 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URI;
+import java.util.Optional;
 
-import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
-import static javax.ws.rs.core.Response.Status.EXPECTATION_FAILED;
-import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
-import static javax.ws.rs.core.Response.Status.NO_CONTENT;
-import static javax.ws.rs.core.Response.Status.OK;
-import static javax.ws.rs.core.Response.Status.REQUEST_TIMEOUT;
+import static javax.ws.rs.core.Response.Status.*;
 
 /**
  * Utilities used by the RESTCONF app.
@@ -84,7 +80,9 @@
         try {
             rootNode = (ObjectNode) mapper.readTree(inputStream);
         } catch (IOException e) {
-            throw new RestconfUtilsException("ERROR: InputStream failed to parse");
+            throw new RestconfException("ERROR: InputStream failed to parse",
+                    e, RestconfError.ErrorTag.OPERATION_FAILED, INTERNAL_SERVER_ERROR,
+                    Optional.empty());
         }
         return rootNode;
     }
@@ -101,7 +99,9 @@
         try {
             inputStream = IOUtils.toInputStream(json);
         } catch (Exception e) {
-            throw new RestconfUtilsException("ERROR: Json Node failed to parse");
+            throw new RestconfException("ERROR: Json Node failed to parse", e,
+                RestconfError.ErrorTag.MALFORMED_MESSAGE, BAD_REQUEST,
+                Optional.empty());
         }
         return inputStream;
     }
@@ -142,13 +142,19 @@
             // CompositeStream --- YangRuntimeService ---> CompositeData.
             CompositeData compositeData = YANG_RUNTIME.decode(compositeStream, context);
             resourceData = compositeData.resourceData();
+        } catch (RestconfException ex) {
+            throw ex;
         } catch (Exception ex) {
             log.error("convertJsonToDataNode failure: {}", ex.getMessage());
             log.debug("convertJsonToDataNode failure", ex);
+            throw new RestconfException("ERROR: JSON cannot be converted to DataNode",
+                    ex, RestconfError.ErrorTag.OPERATION_FAILED, INTERNAL_SERVER_ERROR,
+                    Optional.of(uri.getPath()));
         }
         if (resourceData == null) {
             throw new RestconfException("ERROR: JSON cannot be converted to DataNode",
-                                        INTERNAL_SERVER_ERROR);
+                RestconfError.ErrorTag.DATA_MISSING, CONFLICT,
+                Optional.of(uri.getPath()), Optional.empty());
         }
         return resourceData;
     }
@@ -193,7 +199,8 @@
         }
         if (rootNode == null) {
             throw new RestconfException("ERROR: InputStream can not be convert to ObjectNode",
-                                        INTERNAL_SERVER_ERROR);
+                    null, RestconfError.ErrorTag.DATA_MISSING, CONFLICT,
+                    Optional.empty());
         }
         return rootNode;
     }
diff --git a/apps/restconf/utils/src/main/java/org/onosproject/restconf/utils/exceptions/RestconfUtilsException.java b/apps/restconf/utils/src/main/java/org/onosproject/restconf/utils/exceptions/RestconfUtilsException.java
deleted file mode 100644
index b0db098..0000000
--- a/apps/restconf/utils/src/main/java/org/onosproject/restconf/utils/exceptions/RestconfUtilsException.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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.restconf.utils.exceptions;
-
-/**
- * Represents class of errors related to RESTCONF utilities.
- */
-public class RestconfUtilsException extends RuntimeException {
-
-    /**
-     * Constructs an exception with the specified message.
-     *
-     * @param message the message describing the specific nature of the error
-     */
-    public RestconfUtilsException(String message) {
-        super(message);
-    }
-
-    /**
-     * Constructs an exception with the specified message and the underlying
-     * cause.
-     *
-     * @param message the message describing the specific nature of the error
-     * @param cause   the underlying cause of this error
-     */
-    public RestconfUtilsException(String message, Throwable cause) {
-        super(message, cause);
-    }
-}
diff --git a/apps/route-service/app/src/main/java/org/onosproject/routeservice/impl/RouteManager.java b/apps/route-service/app/src/main/java/org/onosproject/routeservice/impl/RouteManager.java
index 8914a1e..0dc5dbe 100644
--- a/apps/route-service/app/src/main/java/org/onosproject/routeservice/impl/RouteManager.java
+++ b/apps/route-service/app/src/main/java/org/onosproject/routeservice/impl/RouteManager.java
@@ -113,7 +113,9 @@
     @Deactivate
     protected void deactivate() {
         routeMonitor.shutdown();
-        listeners.values().forEach(ListenerQueue::stop);
+        synchronized (this) {
+            listeners.values().forEach(ListenerQueue::stop);
+        }
 
         routeStore.unsetDelegate(delegate);
         hostService.removeListener(hostListener);
@@ -176,6 +178,7 @@
         return routeSets.stream().flatMap(r -> r.routes().stream()).collect(Collectors.toList());
     }
 
+    @Override
     public Collection<RouteTableId> getRouteTables() {
         return routeStore.getRouteTables();
     }
diff --git a/apps/routeradvertisement/src/main/java/org/onosproject/ra/RouterAdvertisementManager.java b/apps/routeradvertisement/src/main/java/org/onosproject/ra/RouterAdvertisementManager.java
index 5f31bdf..9f10728 100644
--- a/apps/routeradvertisement/src/main/java/org/onosproject/ra/RouterAdvertisementManager.java
+++ b/apps/routeradvertisement/src/main/java/org/onosproject/ra/RouterAdvertisementManager.java
@@ -160,14 +160,17 @@
     private final Map<ConnectPoint, Map.Entry<ScheduledFuture<?>, List<InterfaceIpAddress>>> transmitters =
             new LinkedHashMap<>();
 
+    // TODO: should consider using concurrent variants
     @GuardedBy(value = "this")
     private final Map<DeviceId, List<InterfaceIpAddress>> globalPrefixes = new LinkedHashMap<>();
 
     @Override
-    public ImmutableMap<DeviceId, List<InterfaceIpAddress>> getGlobalPrefixes() {
+    public synchronized ImmutableMap<DeviceId, List<InterfaceIpAddress>> getGlobalPrefixes() {
         return ImmutableMap.copyOf(globalPrefixes);
     }
 
+    @SuppressWarnings("GuardedBy")
+    @GuardedBy(value = "this")
     private Function<Interface, Map.Entry<ConnectPoint, List<InterfaceIpAddress>>> prefixGenerator =
             i -> {
                 Map.Entry<ConnectPoint, List<InterfaceIpAddress>> prefixEntry;
@@ -282,6 +285,7 @@
         clearThreadPool();
     }
 
+    @SuppressWarnings("GuardedBy")
     // Loading global prefixes for devices from network configuration
     private synchronized void loadGlobalPrefixConfig() {
         globalPrefixes.clear();
@@ -507,6 +511,7 @@
             solicitHostAddress = ipv6Address;
         }
 
+        @Override
         public void run() {
             // Router Advertisement header filling. Please refer RFC-2461.
             RouterAdvertisement ra = new RouterAdvertisement();
diff --git a/apps/routing-api/src/main/java/org/onosproject/routing/RouterInfo.java b/apps/routing-api/src/main/java/org/onosproject/routing/RouterInfo.java
index 99a6af4..6f2347f 100644
--- a/apps/routing-api/src/main/java/org/onosproject/routing/RouterInfo.java
+++ b/apps/routing-api/src/main/java/org/onosproject/routing/RouterInfo.java
@@ -43,7 +43,7 @@
      */
     public RouterInfo(ConnectPoint controlPlaneConnectPoint, boolean ospfEnabled, Set<String> interfaces) {
         this.controlPlaneConnectPoint = checkNotNull(controlPlaneConnectPoint);
-        this.ospfEnabled = checkNotNull(ospfEnabled);
+        this.ospfEnabled = ospfEnabled;
         this.interfaces = ImmutableSet.copyOf(checkNotNull(interfaces));
     }
 
diff --git a/apps/routing/fpm/app/src/main/java/org/onosproject/routing/fpm/FpmManager.java b/apps/routing/fpm/app/src/main/java/org/onosproject/routing/fpm/FpmManager.java
index dbe58fe..0cf9935 100644
--- a/apps/routing/fpm/app/src/main/java/org/onosproject/routing/fpm/FpmManager.java
+++ b/apps/routing/fpm/app/src/main/java/org/onosproject/routing/fpm/FpmManager.java
@@ -362,6 +362,24 @@
         }
     }
 
+    private boolean routeInDhcpStore(IpPrefix prefix) {
+
+        if (dhcpStore != null) {
+            Collection<FpmRecord> dhcpRecords = dhcpStore.getFpmRecords();
+            return dhcpRecords.stream().anyMatch(record -> record.ipPrefix().equals(prefix));
+        }
+        return false;
+    }
+
+    private boolean routeInRipStore(IpPrefix prefix) {
+
+        if (ripStore != null) {
+            Collection<FpmRecord> ripRecords = ripStore.getFpmRecords();
+            return ripRecords.stream().anyMatch(record -> record.ipPrefix().equals(prefix));
+        }
+        return false;
+    }
+
     private void fpmMessage(FpmPeer peer, FpmHeader fpmMessage) {
         if (fpmMessage.type() == FpmHeader.FPM_TYPE_KEEPALIVE) {
             return;
@@ -400,6 +418,14 @@
 
         IpPrefix prefix = IpPrefix.valueOf(dstAddress, rtNetlink.dstLength());
 
+        // Ignore routes that we sent.
+        if (gateway != null && ((prefix.isIp4() && (gateway.equals(pdPushNextHopIPv4))) ||
+            gateway.equals(pdPushNextHopIPv6))) {
+            if (routeInDhcpStore(prefix) || routeInRipStore(prefix)) {
+                return;
+            }
+        }
+
         List<Route> updates = new LinkedList<>();
         List<Route> withdraws = new LinkedList<>();
 
diff --git a/apps/routing/fpm/app/src/main/java/org/onosproject/routing/fpm/cli/FpmConnectionsList.java b/apps/routing/fpm/app/src/main/java/org/onosproject/routing/fpm/cli/FpmConnectionsList.java
index 33ff5d6..f55f00e 100644
--- a/apps/routing/fpm/app/src/main/java/org/onosproject/routing/fpm/cli/FpmConnectionsList.java
+++ b/apps/routing/fpm/app/src/main/java/org/onosproject/routing/fpm/cli/FpmConnectionsList.java
@@ -41,9 +41,8 @@
     protected void execute() {
         FpmInfoService fpmInfo = get(FpmInfoService.class);
 
-        if (fpmInfo.isPdPushEnabled()) {
-            print("PD Pushing is enabled/disbled.");
-        }
+        print(String.format("PD Pushing is %s.", fpmInfo.isPdPushEnabled() ? "enabled" : "disabled"));
+
         fpmInfo.peers().entrySet().stream()
                 .sorted(Comparator.<Map.Entry<FpmPeer, FpmPeerInfo>, IpAddress>comparing(e -> e.getKey().address())
                         .thenComparing(e -> e.getKey().port()))
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
index 8b240e6..fd57f09 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
@@ -161,8 +161,8 @@
     //  Route path handling
     //////////////////////////////////////
 
-    /* The following three methods represent the three major ways in routing
-     * is triggered in the network
+    /* The following three methods represent the three major ways in which
+     * route-path handling is triggered in the network
      *      a) due to configuration change
      *      b) due to route-added event
      *      c) due to change in the topology
@@ -416,11 +416,11 @@
                 routeChanges = computeRouteChange();
 
                 // deal with linkUp of a seen-before link
-                if (linkUp != null && srManager.isSeenLink(linkUp)) {
-                    if (!srManager.isBidirectional(linkUp)) {
+                if (linkUp != null && srManager.linkHandler.isSeenLink(linkUp)) {
+                    if (!srManager.linkHandler.isBidirectional(linkUp)) {
                         log.warn("Not a bidirectional link yet .. not "
                                 + "processing link {}", linkUp);
-                        srManager.updateSeenLink(linkUp, true);
+                        srManager.linkHandler.updateSeenLink(linkUp, true);
                         populationStatus = Status.ABORTED;
                         return;
                     }
@@ -436,7 +436,7 @@
                 // now that we are past the check for a previously seen link
                 // it is safe to update the store for the linkUp
                 if (linkUp != null) {
-                    srManager.updateSeenLink(linkUp, true);
+                    srManager.linkHandler.updateSeenLink(linkUp, true);
                 }
 
                 //deal with switchDown
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/EcmpShortestPathGraph.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/EcmpShortestPathGraph.java
index 202a564..49bf80f 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/EcmpShortestPathGraph.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/EcmpShortestPathGraph.java
@@ -74,7 +74,7 @@
             currDistance = distanceQueue.poll();
 
             for (Link link : srManager.linkService.getDeviceEgressLinks(sw)) {
-                if (srManager.avoidLink(link)) {
+                if (srManager.linkHandler.avoidLink(link)) {
                     continue;
                 }
                 DeviceId reachedDevice = link.dst().deviceId();
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/HostHandler.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/HostHandler.java
index 1379e79..908a339 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/HostHandler.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/HostHandler.java
@@ -42,6 +42,7 @@
 
 import com.google.common.collect.Sets;
 
+import java.util.HashSet;
 import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.Executors;
@@ -88,6 +89,14 @@
 
     private void processHostAdded(Host host) {
         host.locations().forEach(location -> processHostAddedAtLocation(host, location));
+        // ensure dual-homed host locations have viable uplinks
+        if (host.locations().size() > 1) {
+            host.locations().forEach(loc -> {
+                if (srManager.mastershipService.isLocalMaster(loc.deviceId())) {
+                    srManager.linkHandler.checkUplinksForDualHomedHosts(loc);
+                }
+            });
+        }
     }
 
     void processHostAddedAtLocation(Host host, HostLocation location) {
@@ -276,6 +285,15 @@
                         hostVlanId, ip, false)
             );
         });
+
+        // ensure dual-homed host locations have viable uplinks
+        if (newLocations.size() > prevLocations.size()) {
+            newLocations.forEach(loc -> {
+                if (srManager.mastershipService.isLocalMaster(loc.deviceId())) {
+                    srManager.linkHandler.checkUplinksForDualHomedHosts(loc);
+                }
+            });
+        }
     }
 
     void processHostUpdatedEvent(HostEvent event) {
@@ -640,4 +658,23 @@
                 });
             }));
     }
+
+    /**
+     * Returns the set of portnumbers on the given device that are part of the
+     * locations for dual-homed hosts.
+     *
+     * @param deviceId the given deviceId
+     * @return set of port numbers on given device that are dual-homed host
+     *         locations. May be empty if no dual homed hosts are connected to
+     *         the given device
+     */
+    Set<PortNumber> getDualHomedHostPorts(DeviceId deviceId) {
+        Set<PortNumber> dualHomedLocations = new HashSet<>();
+        srManager.hostService.getConnectedHosts(deviceId).stream()
+            .filter(host -> host.locations().size() == 2)
+            .forEach(host -> host.locations().stream()
+                     .filter(loc -> loc.deviceId().equals(deviceId))
+                        .forEach(loc -> dualHomedLocations.add(loc.port())));
+        return dualHomedLocations;
+    }
 }
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/LinkHandler.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/LinkHandler.java
new file mode 100644
index 0000000..ef9fb38
--- /dev/null
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/LinkHandler.java
@@ -0,0 +1,563 @@
+/*
+ * Copyright 2018-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.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.HostLocation;
+import org.onosproject.net.Link;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.link.LinkService;
+import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
+import org.onosproject.segmentrouting.config.DeviceConfiguration;
+import org.onosproject.segmentrouting.grouphandler.DefaultGroupHandler;
+import org.onosproject.store.service.EventuallyConsistentMap;
+import org.onosproject.store.service.EventuallyConsistentMapBuilder;
+import org.onosproject.store.service.WallClockTimestamp;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.Sets;
+
+public class LinkHandler {
+    private static final Logger log = LoggerFactory.getLogger(LinkHandler.class);
+    protected final SegmentRoutingManager srManager;
+    protected LinkService linkService;
+
+    // Local store for all links seen and their present status, used for
+    // optimized routing. The existence of the link in the keys is enough to
+    // know
+    // if the link has been "seen-before" by this instance of the controller.
+    // The boolean value indicates if the link is currently up or not.
+    // XXX Currently the optimized routing logic depends on "forgetting" a link
+    // when a switch goes down, but "remembering" it when only the link goes
+    // down.
+    // Consider changing this logic so we can use the Link Service instead of
+    // a local cache.
+    private Map<Link, Boolean> seenLinks = new ConcurrentHashMap<>();
+
+    private EventuallyConsistentMap<DeviceId, Set<PortNumber>> downedPortStore = null;
+
+    /**
+     * Constructs the LinkHandler.
+     *
+     * @param srManager Segment Routing manager
+     */
+    LinkHandler(SegmentRoutingManager srManager) {
+        this.srManager = srManager;
+        linkService = srManager.linkService;
+        log.debug("Creating EC map downedportstore");
+        EventuallyConsistentMapBuilder<DeviceId, Set<PortNumber>> downedPortsMapBuilder
+                = srManager.storageService.eventuallyConsistentMapBuilder();
+        downedPortStore = downedPortsMapBuilder.withName("downedportstore")
+                .withSerializer(srManager.createSerializer())
+                .withTimestampProvider((k, v) -> new WallClockTimestamp())
+                .build();
+        log.trace("Current size {}", downedPortStore.size());
+    }
+
+    /**
+     * Constructs the LinkHandler for unit-testing.
+     *
+     * @param srManager SegmentRoutingManager
+     * @param linkService LinkService
+     */
+    LinkHandler(SegmentRoutingManager srManager, LinkService linkService) {
+        this.srManager = srManager;
+        this.linkService = linkService;
+    }
+
+    /**
+     * Preprocessing of added link before being sent for route-path handling.
+     * Also performs post processing of link.
+     *
+     * @param link the link to be processed
+     */
+    void processLinkAdded(Link link) {
+        log.info("** LINK ADDED {}", link.toString());
+        if (!isLinkValid(link)) {
+            return;
+        }
+        if (!srManager.deviceConfiguration
+                .isConfigured(link.src().deviceId())) {
+            updateSeenLink(link, true);
+            // XXX revisit - what about devicePortMap
+            log.warn("Source device of this link is not configured.. "
+                    + "not processing further");
+            return;
+        }
+
+        // Irrespective of whether the local is a MASTER or not for this device,
+        // create group handler instance and push default TTP flow rules if
+        // needed,
+        // as in a multi-instance setup, instances can initiate groups for any
+        // device.
+        DefaultGroupHandler groupHandler = srManager.groupHandlerMap
+                .get(link.src().deviceId());
+        if (groupHandler != null) {
+            groupHandler.portUpForLink(link);
+        } else {
+            // XXX revisit/cleanup
+            Device device = srManager.deviceService.getDevice(link.src().deviceId());
+            if (device != null) {
+                log.warn("processLinkAdded: Link Added "
+                        + "Notification without Device Added "
+                        + "event, still handling it");
+                srManager.processDeviceAdded(device);
+                groupHandler = srManager.groupHandlerMap.get(link.src().deviceId());
+                groupHandler.portUpForLink(link);
+            }
+        }
+
+        /*
+         // process link only if it is bidirectional
+         if (!isBidirectional(link)) {
+            log.debug("Link not bidirectional.. waiting for other direction " +
+                      "src {} --> dst {} ", link.dst(), link.src());
+            // note that if we are not processing for routing, it should at least
+            // be considered a seen-link
+            updateSeenLink(link, true); return;
+          }
+         //TODO ensure that rehash is still done correctly even if link is not processed for
+         //rerouting - perhaps rehash in both directions when it ultimately becomes bidi?
+         */
+
+        log.debug("Starting optimized route-path processing for added link "
+                + "{} --> {}", link.src(), link.dst());
+        boolean seenBefore = isSeenLink(link);
+        // seenLink updates will be done after route-path changes
+        srManager.defaultRoutingHandler
+                .populateRoutingRulesForLinkStatusChange(null, link, null);
+
+        if (srManager.mastershipService.isLocalMaster(link.src().deviceId())) {
+            // handle edge-ports for dual-homed hosts
+            updateDualHomedHostPorts(link, true);
+
+            // It's possible that linkUp causes no route-path change as ECMP
+            // graph does
+            // not change if the link is a parallel link (same src-dst as
+            // another link.
+            // However we still need to update ECMP hash groups to include new
+            // buckets
+            // for the link that has come up.
+            if (groupHandler != null) {
+                if (!seenBefore && isParallelLink(link)) {
+                    // if link seen first time, we need to ensure hash-groups have
+                    // all ports
+                    log.debug("Attempting retryHash for paralled first-time link {}",
+                            link);
+                    groupHandler.retryHash(link, false, true);
+                } else {
+                    // seen before-link
+                    if (isParallelLink(link)) {
+                        log.debug("Attempting retryHash for paralled seen-before "
+                                + "link {}", link);
+                        groupHandler.retryHash(link, false, false);
+                    }
+                }
+            }
+        }
+
+        srManager.mcastHandler.init();
+    }
+
+    /**
+     * Preprocessing of removed link before being sent for route-path handling.
+     * Also performs post processing of link.
+     *
+     * @param link the link to be processed
+     */
+    void processLinkRemoved(Link link) {
+        log.info("** LINK REMOVED {}", link.toString());
+        if (!isLinkValid(link)) {
+            return;
+        }
+        // when removing links, update seen links first, before doing route-path
+        // changes
+        updateSeenLink(link, false);
+
+        // device availability check helps to ensure that multiple link-removed
+        // events are actually treated as a single switch removed event.
+        // purgeSeenLink is necessary so we do rerouting (instead of rehashing)
+        // when switch comes back.
+        if (link.src().elementId() instanceof DeviceId
+                && !srManager.deviceService.isAvailable(link.src().deviceId())) {
+            purgeSeenLink(link);
+            return;
+        }
+        if (link.dst().elementId() instanceof DeviceId
+                && !srManager.deviceService.isAvailable(link.dst().deviceId())) {
+            purgeSeenLink(link);
+            return;
+        }
+
+        // handle edge-ports for dual-homed hosts
+        if (srManager.mastershipService.isLocalMaster(link.src().deviceId())) {
+            updateDualHomedHostPorts(link, false);
+        }
+
+        log.debug("Starting optimized route-path processing for removed link "
+                + "{} --> {}", link.src(), link.dst());
+        srManager.defaultRoutingHandler
+                .populateRoutingRulesForLinkStatusChange(link, null, null);
+
+        // update local groupHandler stores
+        DefaultGroupHandler groupHandler = srManager.groupHandlerMap
+                .get(link.src().deviceId());
+        if (groupHandler != null) {
+            if (srManager.mastershipService.isLocalMaster(link.src().deviceId())
+                    && isParallelLink(link)) {
+                log.debug("* retrying hash for parallel link removed:{}", link);
+                groupHandler.retryHash(link, true, false);
+            } else {
+                log.debug("Not attempting retry-hash for link removed: {} .. {}",
+                          link,
+                          (srManager.mastershipService.isLocalMaster(link.src()
+                                  .deviceId())) ? "not parallel"
+                                                : "not master");
+            }
+            // ensure local stores are updated
+            groupHandler.portDown(link.src().port());
+        } else {
+            log.warn("group handler not found for dev:{} when removing link: {}",
+                     link.src().deviceId(), link);
+        }
+
+        srManager.mcastHandler.processLinkDown(link);
+    }
+
+    /**
+     * Checks validity of link. Examples of invalid links include
+     * indirect-links, links between ports on the same switch, and more.
+     *
+     * @param link the link to be processed
+     * @return true if valid link
+     */
+    private boolean isLinkValid(Link link) {
+        if (link.type() != Link.Type.DIRECT) {
+            // NOTE: A DIRECT link might be transiently marked as INDIRECT
+            // if BDDP is received before LLDP. We can safely ignore that
+            // until the LLDP is received and the link is marked as DIRECT.
+            log.info("Ignore link {}->{}. Link type is {} instead of DIRECT.",
+                     link.src(), link.dst(), link.type());
+            return false;
+        }
+        DeviceId srcId = link.src().deviceId();
+        DeviceId dstId = link.dst().deviceId();
+        if (srcId.equals(dstId)) {
+            log.warn("Links between ports on the same switch are not "
+                    + "allowed .. ignoring link {}", link);
+            return false;
+        }
+        DeviceConfiguration devConfig = srManager.deviceConfiguration;
+        try {
+            if (!devConfig.isEdgeDevice(srcId)
+                    && !devConfig.isEdgeDevice(dstId)) {
+                // ignore links between spines
+                // XXX revisit when handling multi-stage fabrics
+                log.warn("Links between spines not allowed...ignoring "
+                        + "link {}", link);
+                return false;
+            }
+            if (devConfig.isEdgeDevice(srcId)
+                    && devConfig.isEdgeDevice(dstId)) {
+                // ignore links between leaves if they are not pair-links
+                // XXX revisit if removing pair-link config or allowing more than
+                // one pair-link
+                if (devConfig.getPairDeviceId(srcId).equals(dstId)
+                        && devConfig.getPairLocalPort(srcId)
+                                .equals(link.src().port())
+                        && devConfig.getPairLocalPort(dstId)
+                                .equals(link.dst().port())) {
+                    // found pair link - allow it
+                    return true;
+                } else {
+                    log.warn("Links between leaves other than pair-links are "
+                            + "not allowed...ignoring link {}", link);
+                    return false;
+                }
+            }
+        } catch (DeviceConfigNotFoundException e) {
+            // We still want to count the links in seenLinks even though there
+            // is no config. So we let it return true
+            log.warn("Could not check validity of link {} as subtending devices "
+                    + "are not yet configured", link);
+        }
+        return true;
+    }
+
+    /**
+     * Administratively enables or disables edge ports if the link that was
+     * added or removed was the only uplink port from an edge device. Only edge
+     * ports that belong to dual-homed hosts are considered.
+     *
+     * @param link the link to be processed
+     * @param added true if link was added, false if link was removed
+     */
+    private void updateDualHomedHostPorts(Link link, boolean added) {
+        if (!lastUplink(link)) {
+            return;
+        }
+        if (added) {
+            // re-enable previously disabled ports on this dev
+            Set<PortNumber> p = downedPortStore.remove(link.src().deviceId());
+            if (p != null) {
+                log.warn("Link src {} -->dst {} added is the first uplink, "
+                        + "enabling dual homed ports: {}", link.src().deviceId(),
+                     link.dst().deviceId(), (p.isEmpty()) ? "no ports" : p);
+                p.forEach(pnum -> srManager.deviceAdminService
+                        .changePortState(link.src().deviceId(), pnum, true));
+            }
+        } else {
+            // find dual homed hosts on this dev to disable
+            Set<PortNumber> dhp = srManager.hostHandler
+                    .getDualHomedHostPorts(link.src().deviceId());
+            log.warn("Link src {} -->dst {} removed was the last uplink, "
+                    + "disabling  dual homed ports:  {}", link.src().deviceId(),
+                     link.dst().deviceId(), (dhp.isEmpty()) ? "no ports" : dhp);
+            dhp.forEach(pnum -> srManager.deviceAdminService
+                        .changePortState(link.src().deviceId(), pnum, false));
+            if (!dhp.isEmpty()) {
+                // update global store
+                Set<PortNumber> p = downedPortStore.get(link.src().deviceId());
+                if (p == null) {
+                    p = dhp;
+                } else {
+                    p.addAll(dhp);
+                }
+                downedPortStore.put(link.src().deviceId(), p);
+            }
+        }
+    }
+
+    /**
+     * Returns true if given link is the last active uplink from src-device of
+     * link. An uplink is defined as a unidirectional link with src as
+     * edgeRouter and dst as non-edgeRouter.
+     *
+     * @param link
+     * @return true if given link is-the-first/was-the-last uplink from the src
+     *         device
+     */
+    private boolean lastUplink(Link link) {
+        DeviceConfiguration devConfig = srManager.deviceConfiguration;
+        try {
+            if (!devConfig.isEdgeDevice(link.src().deviceId())) {
+                return false;
+            }
+            Set<Link> devLinks = srManager.linkService
+                    .getDeviceLinks(link.src().deviceId());
+            boolean foundOtherUplink = false;
+            for (Link l : devLinks) {
+                if (devConfig.isEdgeDevice(l.dst().deviceId()) || l.equals(link)
+                        || l.state() == Link.State.INACTIVE) {
+                    continue;
+                }
+                foundOtherUplink = true;
+                break;
+            }
+            if (!foundOtherUplink) {
+                return true;
+            }
+        } catch (DeviceConfigNotFoundException e) {
+            log.warn("Unable to determine if link is last uplink"
+                    + e.getMessage());
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if this controller instance has seen this link before. The
+     * link may not be currently up, but as long as the link had been seen
+     * before this method will return true. The one exception is when the link
+     * was indeed seen before, but this controller instance was forced to forget
+     * it by a call to purgeSeenLink method.
+     *
+     * @param link the infrastructure link being queried
+     * @return true if this controller instance has seen this link before
+     */
+    boolean isSeenLink(Link link) {
+        return seenLinks.containsKey(link);
+    }
+
+    /**
+     * Updates the seen link store. Updates can be for links that are currently
+     * available or not.
+     *
+     * @param link the link to update in the seen-link local store
+     * @param up the status of the link, true if up, false if down
+     */
+    void updateSeenLink(Link link, boolean up) {
+        seenLinks.put(link, up);
+    }
+
+    /**
+     * Returns the status of a seen-link (up or down). If the link has not been
+     * seen-before, a null object is returned.
+     *
+     * @param link the infrastructure link being queried
+     * @return null if the link was not seen-before; true if the seen-link is
+     *         up; false if the seen-link is down
+     */
+    private Boolean isSeenLinkUp(Link link) {
+        return seenLinks.get(link);
+    }
+
+    /**
+     * Makes this controller instance forget a previously seen before link.
+     *
+     * @param link the infrastructure link to purge
+     */
+    private void purgeSeenLink(Link link) {
+        seenLinks.remove(link);
+    }
+
+    /**
+     * Returns the status of a link as parallel link. A parallel link is defined
+     * as a link which has common src and dst switches as another seen-link that
+     * is currently enabled. It is not necessary for the link being queried to
+     * be a seen-link.
+     *
+     * @param link the infrastructure link being queried
+     * @return true if a seen-link exists that is up, and shares the same src
+     *         and dst switches as the link being queried
+     */
+    private boolean isParallelLink(Link link) {
+        for (Entry<Link, Boolean> seen : seenLinks.entrySet()) {
+            Link seenLink = seen.getKey();
+            if (seenLink.equals(link)) {
+                continue;
+            }
+            if (seenLink.src().deviceId().equals(link.src().deviceId())
+                    && seenLink.dst().deviceId().equals(link.dst().deviceId())
+                    && seen.getValue()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if the link being queried is a bidirectional link. A bidi
+     * link is defined as a link, whose reverse link - ie. the link in the
+     * reverse direction - has been seen-before and is up. It is not necessary
+     * for the link being queried to be a seen-link.
+     *
+     * @param link the infrastructure link being queried
+     * @return true if another unidirectional link exists in the reverse
+     *         direction, has been seen-before and is up
+     */
+    boolean isBidirectional(Link link) {
+        Link reverseLink = linkService.getLink(link.dst(), link.src());
+        if (reverseLink == null) {
+            return false;
+        }
+        Boolean result = isSeenLinkUp(reverseLink);
+        if (result == null) {
+            return false;
+        }
+        return result.booleanValue();
+    }
+
+    /**
+     * Determines if the given link should be avoided in routing calculations by
+     * policy or design.
+     *
+     * @param link the infrastructure link being queried
+     * @return true if link should be avoided
+     */
+    boolean avoidLink(Link link) {
+        // XXX currently only avoids all pair-links. In the future can be
+        // extended to avoid any generic link
+        DeviceId src = link.src().deviceId();
+        PortNumber srcPort = link.src().port();
+        DeviceConfiguration devConfig = srManager.deviceConfiguration;
+        if (devConfig == null || !devConfig.isConfigured(src)) {
+            log.warn("Device {} not configured..cannot avoid link {}", src,
+                     link);
+            return false;
+        }
+        DeviceId pairDev;
+        PortNumber pairLocalPort, pairRemotePort = null;
+        try {
+            pairDev = devConfig.getPairDeviceId(src);
+            pairLocalPort = devConfig.getPairLocalPort(src);
+            if (pairDev != null) {
+                pairRemotePort = devConfig
+                        .getPairLocalPort(pairDev);
+            }
+        } catch (DeviceConfigNotFoundException e) {
+            log.warn("Pair dev for dev {} not configured..cannot avoid link {}",
+                     src, link);
+            return false;
+        }
+
+        return srcPort.equals(pairLocalPort)
+                && link.dst().deviceId().equals(pairDev)
+                && link.dst().port().equals(pairRemotePort);
+    }
+
+    /**
+     * Cleans up internal LinkHandler stores.
+     *
+     * @param device the device that has been removed
+     */
+    void processDeviceRemoved(Device device) {
+        seenLinks.keySet()
+                .removeIf(key -> key.src().deviceId().equals(device.id())
+                        || key.dst().deviceId().equals(device.id()));
+    }
+
+    /**
+     * Administratively disables the host location switchport if the edge device
+     * has no viable uplinks.
+     *
+     * @param loc one of the locations of the dual-homed host
+     */
+    void checkUplinksForDualHomedHosts(HostLocation loc) {
+        try {
+            for (Link l : srManager.linkService.getDeviceLinks(loc.deviceId())) {
+                if (srManager.deviceConfiguration.isEdgeDevice(l.dst().deviceId())
+                        || l.state() == Link.State.INACTIVE) {
+                    continue;
+                }
+                // found valid uplink - so, nothing to do
+                return;
+            }
+        } catch (DeviceConfigNotFoundException e) {
+            log.warn("Could not check for valid uplinks due to missing device"
+                    + "config " + e.getMessage());
+            return;
+        }
+        log.warn("Dual homed host location {} has no valid uplinks; "
+                + "disabling  dual homed port", loc);
+        srManager.deviceAdminService.changePortState(loc.deviceId(), loc.port(),
+                                                     false);
+        Set<PortNumber> p = downedPortStore.get(loc.deviceId());
+        if (p == null) {
+            p = Sets.newHashSet(loc.port());
+        } else {
+            p.add(loc.port());
+        }
+        downedPortStore.put(loc.deviceId(), p);
+    }
+
+}
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/McastHandler.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/McastHandler.java
index 80affc1..b0be4e3 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/McastHandler.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/McastHandler.java
@@ -411,7 +411,7 @@
                 log.info("NextObj for {}/{} already exists. Abort", deviceId, port);
                 return;
             }
-            portBuilder.addAll(existingPorts).add(port).build();
+            portBuilder.addAll(existingPorts).add(port);
         }
         // Create, store and apply the new nextObj and fwdObj
         ObjectiveContext context = new DefaultObjectiveContext(
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index 0725e1b..3242866 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -60,6 +60,7 @@
 import org.onosproject.net.config.basics.InterfaceConfig;
 import org.onosproject.net.config.basics.McastConfig;
 import org.onosproject.net.config.basics.SubjectFactories;
+import org.onosproject.net.device.DeviceAdminService;
 import org.onosproject.net.device.DeviceEvent;
 import org.onosproject.net.device.DeviceListener;
 import org.onosproject.net.device.DeviceService;
@@ -119,7 +120,6 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
@@ -167,6 +167,9 @@
     DeviceService deviceService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    DeviceAdminService deviceAdminService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     public FlowObjectiveService flowObjectiveService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -212,9 +215,10 @@
     private InternalDeviceListener deviceListener = null;
     private AppConfigHandler appCfgHandler = null;
     XConnectHandler xConnectHandler = null;
-    private McastHandler mcastHandler = null;
-    private HostHandler hostHandler = null;
+    McastHandler mcastHandler = null;
+    HostHandler hostHandler = null;
     private RouteHandler routeHandler = null;
+    LinkHandler linkHandler = null;
     private SegmentRoutingNeighbourDispatcher neighbourHandler = null;
     private L2TunnelHandler l2TunnelHandler = null;
     private InternalEventHandler eventHandler = new InternalEventHandler();
@@ -230,7 +234,7 @@
     private static ScheduledFuture<?> eventHandlerFuture = null;
     @SuppressWarnings("rawtypes")
     private ConcurrentLinkedQueue<Event> eventQueue = new ConcurrentLinkedQueue<>();
-    private Map<DeviceId, DefaultGroupHandler> groupHandlerMap =
+    Map<DeviceId, DefaultGroupHandler> groupHandlerMap =
             new ConcurrentHashMap<>();
     /**
      * Per device next objective ID store with (device id + destination set) as key.
@@ -251,16 +255,6 @@
     private EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
             portNextObjStore = null;
 
-    // Local store for all links seen and their present status, used for
-    // optimized routing. The existence of the link in the keys is enough to know
-    // if the link has been "seen-before" by this instance of the controller.
-    // The boolean value indicates if the link is currently up or not.
-    // XXX Currently the optimized routing logic depends on "forgetting" a link
-    // when a switch goes down, but "remembering" it when only the link goes down.
-    // Consider changing this logic so we can use the Link Service instead of
-    // a local cache.
-    private Map<Link, Boolean> seenLinks = new ConcurrentHashMap<>();
-
     private EventuallyConsistentMap<String, Tunnel> tunnelStore = null;
     private EventuallyConsistentMap<String, Policy> policyStore = null;
 
@@ -412,6 +406,7 @@
         xConnectHandler = new XConnectHandler(this);
         mcastHandler = new McastHandler(this);
         hostHandler = new HostHandler(this);
+        linkHandler = new LinkHandler(this);
         routeHandler = new RouteHandler(this);
         neighbourHandler = new SegmentRoutingNeighbourDispatcher(this);
         l2TunnelHandler = new L2TunnelHandler(this);
@@ -437,7 +432,7 @@
         log.info("Started");
     }
 
-    private KryoNamespace.Builder createSerializer() {
+    KryoNamespace.Builder createSerializer() {
         return new KryoNamespace.Builder()
                 .register(KryoNamespaces.API)
                 .register(DestinationSetNextObjectiveStoreKey.class,
@@ -921,135 +916,6 @@
         return defaultRoutingHandler;
     }
 
-    /**
-     * Returns true if this controller instance has seen this link before. The
-     * link may not be currently up, but as long as the link had been seen before
-     * this method will return true. The one exception is when the link was
-     * indeed seen before, but this controller instance was forced to forget it
-     * by a call to purgeSeenLink method.
-     *
-     * @param link the infrastructure link being queried
-     * @return true if this controller instance has seen this link before
-     */
-    boolean isSeenLink(Link link) {
-        return seenLinks.containsKey(link);
-    }
-
-    /**
-     * Updates the seen link store. Updates can be for links that are currently
-     * available or not.
-     *
-     * @param link the link to update in the seen-link local store
-     * @param up the status of the link, true if up, false if down
-     */
-    void updateSeenLink(Link link, boolean up) {
-        seenLinks.put(link, up);
-    }
-
-    /**
-     * Returns the status of a seen-link (up or down). If the link has not
-     * been seen-before, a null object is returned.
-     *
-     * @param link the infrastructure link being queried
-     * @return null if the link was not seen-before;
-     *         true if the seen-link is up;
-     *         false if the seen-link is down
-     */
-    private Boolean isSeenLinkUp(Link link) {
-        return seenLinks.get(link);
-    }
-
-    /**
-     * Makes this controller instance forget a previously seen before link.
-     *
-     * @param link the infrastructure link to purge
-     */
-    private void purgeSeenLink(Link link) {
-        seenLinks.remove(link);
-    }
-
-    /**
-     * Returns the status of a link as parallel link. A parallel link
-     * is defined as a link which has common src and dst switches as another
-     * seen-link that is currently enabled. It is not necessary for the link being
-     * queried to be a seen-link.
-     *
-     *  @param link the infrastructure link being queried
-     *  @return true if a seen-link exists that is up, and shares the
-     *          same src and dst switches as the link being queried
-     */
-    private boolean isParallelLink(Link link) {
-        for (Entry<Link, Boolean> seen : seenLinks.entrySet()) {
-            Link seenLink = seen.getKey();
-           if (seenLink.equals(link)) {
-               continue;
-           }
-           if (seenLink.src().deviceId().equals(link.src().deviceId()) &&
-                   seenLink.dst().deviceId().equals(link.dst().deviceId()) &&
-                   seen.getValue()) {
-               return true;
-           }
-        }
-        return false;
-    }
-
-    /**
-     * Returns true if the link being queried is a bidirectional link. A bidi
-     * link is defined as a link, whose reverse link - ie. the link in the reverse
-     * direction - has been seen-before and is up. It is not necessary for the link
-     * being queried to be a seen-link.
-     *
-     * @param link the infrastructure link being queried
-     * @return true if another unidirectional link exists in the reverse direction,
-     *              has been seen-before and is up
-     */
-    boolean isBidirectional(Link link) {
-        Link reverseLink = linkService.getLink(link.dst(), link.src());
-        if (reverseLink == null) {
-            return false;
-        }
-        Boolean result = isSeenLinkUp(reverseLink);
-        if (result == null) {
-            return false;
-        }
-        return result.booleanValue();
-    }
-
-    /**
-     * Determines if the given link should be avoided in routing calculations
-     * by policy or design.
-     *
-     * @param link the infrastructure link being queried
-     * @return true if link should be avoided
-     */
-    boolean avoidLink(Link link) {
-        // XXX currently only avoids all pair-links. In the future can be
-        // extended to avoid any generic link
-        DeviceId src = link.src().deviceId();
-        PortNumber srcPort = link.src().port();
-        if (deviceConfiguration == null || !deviceConfiguration.isConfigured(src)) {
-            log.warn("Device {} not configured..cannot avoid link {}", src, link);
-            return false;
-        }
-        DeviceId pairDev;
-        PortNumber pairLocalPort, pairRemotePort = null;
-        try {
-            pairDev = deviceConfiguration.getPairDeviceId(src);
-            pairLocalPort = deviceConfiguration.getPairLocalPort(src);
-            if (pairDev != null) {
-                pairRemotePort = deviceConfiguration.getPairLocalPort(pairDev);
-            }
-        } catch (DeviceConfigNotFoundException e) {
-            log.warn("Pair dev for dev {} not configured..cannot avoid link {}",
-                     src, link);
-            return false;
-        }
-
-        return srcPort.equals(pairLocalPort) &&
-                link.dst().deviceId().equals(pairDev) &&
-                link.dst().port().equals(pairRemotePort);
-    }
-
     private class InternalPacketProcessor implements PacketProcessor {
         @Override
         public void process(PacketContext context) {
@@ -1174,28 +1040,9 @@
                         // Note: do not update seenLinks here, otherwise every
                         // link, even one seen for the first time, will be appear
                         // to be a previously seen link
-                        processLinkAdded((Link) event.subject());
+                        linkHandler.processLinkAdded((Link) event.subject());
                     } else if (event.type() == LinkEvent.Type.LINK_REMOVED) {
-                        Link linkRemoved = (Link) event.subject();
-                        if (linkRemoved.type() == Link.Type.DIRECT) {
-                            updateSeenLink(linkRemoved, false);
-                        }
-                        // device availability check helps to ensure that
-                        // multiple link-removed events are actually treated as a
-                        // single switch removed event. purgeSeenLink is necessary
-                        // so we do rerouting (instead of rehashing) when switch
-                        // comes back.
-                        if (linkRemoved.src().elementId() instanceof DeviceId &&
-                                !deviceService.isAvailable(linkRemoved.src().deviceId())) {
-                            purgeSeenLink(linkRemoved);
-                            continue;
-                        }
-                        if (linkRemoved.dst().elementId() instanceof DeviceId &&
-                                !deviceService.isAvailable(linkRemoved.dst().deviceId())) {
-                            purgeSeenLink(linkRemoved);
-                            continue;
-                        }
-                        processLinkRemoved((Link) event.subject());
+                        linkHandler.processLinkRemoved((Link) event.subject());
                     } else if (event.type() == DeviceEvent.Type.DEVICE_ADDED ||
                             event.type() == DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED ||
                             event.type() == DeviceEvent.Type.DEVICE_UPDATED) {
@@ -1238,164 +1085,7 @@
         }
     }
 
-    private void processLinkAdded(Link link) {
-        log.info("** LINK ADDED {}", link.toString());
-        if (!isLinkValid(link)) {
-            return;
-        }
-        if (!deviceConfiguration.isConfigured(link.src().deviceId())) {
-            updateSeenLink(link, true);
-            // XXX revisit - what about devicePortMap
-            log.warn("Source device of this link is not configured.. "
-                    + "not processing further");
-            return;
-        }
-
-        //Irrespective of whether the local is a MASTER or not for this device,
-        //create group handler instance and push default TTP flow rules if needed,
-        //as in a multi-instance setup, instances can initiate groups for any device.
-        DefaultGroupHandler groupHandler = groupHandlerMap.get(link.src()
-                .deviceId());
-        if (groupHandler != null) {
-            groupHandler.portUpForLink(link);
-        } else {
-            // XXX revisit/cleanup
-            Device device = deviceService.getDevice(link.src().deviceId());
-            if (device != null) {
-                log.warn("processLinkAdded: Link Added "
-                        + "Notification without Device Added "
-                        + "event, still handling it");
-                processDeviceAdded(device);
-                groupHandler = groupHandlerMap.get(link.src()
-                                                   .deviceId());
-                groupHandler.portUpForLink(link);
-            }
-        }
-
-        /*// process link only if it is bidirectional
-        if (!isBidirectional(link)) {
-            log.debug("Link not bidirectional.. waiting for other direction "
-                    + "src {} --> dst {} ", link.dst(), link.src());
-            // note that if we are not processing for routing, it should at least
-            // be considered a seen-link
-            updateSeenLink(link, true);
-            return;
-        }
-         TO DO this ensure that rehash is still done correctly even if link is
-         not processed for rerouting - perhaps rehash in both directions when
-         it ultimately becomes bidi?
-        */
-
-        log.debug("Starting optimized route population process for link "
-                + "{} --> {}", link.src(), link.dst());
-        boolean seenBefore = isSeenLink(link);
-        defaultRoutingHandler.populateRoutingRulesForLinkStatusChange(null, link, null);
-
-        // It's possible that linkUp causes no route-path change as ECMP graph does
-        // not change if the link is a parallel link (same src-dst as another link.
-        // However we still need to update ECMP hash groups to include new buckets
-        // for the link that has come up.
-        if (mastershipService.isLocalMaster(link.src().deviceId())) {
-            if (!seenBefore && isParallelLink(link)) {
-                // if link seen first time, we need to ensure hash-groups have all ports
-                log.debug("Attempting retryHash for paralled first-time link {}", link);
-                groupHandler.retryHash(link, false, true);
-            } else {
-                //seen before-link
-                if (isParallelLink(link)) {
-                    log.debug("Attempting retryHash for paralled seen-before "
-                            + "link {}", link);
-                    groupHandler.retryHash(link, false, false);
-                }
-            }
-        }
-
-        mcastHandler.init();
-    }
-
-    private void processLinkRemoved(Link link) {
-        log.info("** LINK REMOVED {}", link.toString());
-        if (!isLinkValid(link)) {
-            return;
-        }
-        defaultRoutingHandler.populateRoutingRulesForLinkStatusChange(link, null, null);
-
-        // update local groupHandler stores
-        DefaultGroupHandler groupHandler = groupHandlerMap.get(link.src().deviceId());
-        if (groupHandler != null) {
-            if (mastershipService.isLocalMaster(link.src().deviceId()) &&
-                    isParallelLink(link)) {
-                log.debug("* retrying hash for parallel link removed:{}", link);
-                groupHandler.retryHash(link, true, false);
-            } else {
-                log.debug("Not attempting retry-hash for link removed: {} .. {}", link,
-                          (mastershipService.isLocalMaster(link.src().deviceId()))
-                                  ? "not parallel" : "not master");
-            }
-            // ensure local stores are updated
-            groupHandler.portDown(link.src().port());
-        } else {
-            log.warn("group handler not found for dev:{} when removing link: {}",
-                     link.src().deviceId(), link);
-        }
-
-        mcastHandler.processLinkDown(link);
-        l2TunnelHandler.processLinkDown(link);
-    }
-
-    private boolean isLinkValid(Link link) {
-        if (link.type() != Link.Type.DIRECT) {
-            // NOTE: A DIRECT link might be transiently marked as INDIRECT
-            // if BDDP is received before LLDP. We can safely ignore that
-            // until the LLDP is received and the link is marked as DIRECT.
-            log.info("Ignore link {}->{}. Link type is {} instead of DIRECT.",
-                     link.src(), link.dst(), link.type());
-            return false;
-        }
-        DeviceId srcId = link.src().deviceId();
-        DeviceId dstId = link.dst().deviceId();
-        if (srcId.equals(dstId)) {
-            log.warn("Links between ports on the same switch are not "
-                    + "allowed .. ignoring link {}", link);
-            return false;
-        }
-        try {
-            if (!deviceConfiguration.isEdgeDevice(srcId)
-                    && !deviceConfiguration.isEdgeDevice(dstId)) {
-                // ignore links between spines
-                // XXX revisit when handling multi-stage fabrics
-                log.warn("Links between spines not allowed...ignoring "
-                        + "link {}", link);
-                return false;
-            }
-            if (deviceConfiguration.isEdgeDevice(srcId)
-                    && deviceConfiguration.isEdgeDevice(dstId)) {
-                // ignore links between leaves if they are not pair-links
-                // XXX revisit if removing pair-link config or allowing more than
-                // one pair-link
-                if (deviceConfiguration.getPairDeviceId(srcId).equals(dstId)
-                        && deviceConfiguration.getPairLocalPort(srcId)
-                                .equals(link.src().port())
-                        && deviceConfiguration.getPairLocalPort(dstId)
-                                .equals(link.dst().port())) {
-                    // found pair link - allow it
-                    return true;
-                } else {
-                    log.warn("Links between leaves other than pair-links are "
-                            + "not allowed...ignoring link {}", link);
-                    return false;
-                }
-            }
-        } catch (DeviceConfigNotFoundException e) {
-            // We still want to count the links in seenLinks even though there
-            // is no config. So we let it return true
-            log.warn("Could not check validity of link {} as subtending devices "
-                    + "are not yet configured", link);
-        }
-        return true;
-    }
-
-    private void processDeviceAdded(Device device) {
+    void processDeviceAdded(Device device) {
         log.info("** DEVICE ADDED with ID {}", device.id());
 
         // NOTE: Punt ARP/NDP even when the device is not configured.
@@ -1461,9 +1151,7 @@
         portNextObjStore.entrySet().stream()
                 .filter(entry -> entry.getKey().deviceId().equals(device.id()))
                 .forEach(entry -> portNextObjStore.remove(entry.getKey()));
-
-        seenLinks.keySet().removeIf(key -> key.src().deviceId().equals(device.id()) ||
-                key.dst().deviceId().equals(device.id()));
+        linkHandler.processDeviceRemoved(device);
 
         DefaultGroupHandler gh = groupHandlerMap.remove(device.id());
         if (gh != null) {
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingNeighbourDispatcher.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingNeighbourDispatcher.java
index cc815da..f170fae 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingNeighbourDispatcher.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingNeighbourDispatcher.java
@@ -42,7 +42,8 @@
 
     @Override
     public void handleMessage(NeighbourMessageContext context, HostService hostService) {
-        log.trace("Received a {} packet {}", context.protocol(), context.packet());
+        log.trace("Received {} packet on {}: {}", context.protocol(),
+                  context.inPort(), context.packet());
         switch (context.protocol()) {
             case ARP:
                 if (this.manager.arpHandler != null) {
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/PwaasConfig.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/PwaasConfig.java
index ed2c067..18fc738 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/PwaasConfig.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/PwaasConfig.java
@@ -122,13 +122,13 @@
                     .map(this::getPwDescription)
                     .collect(Collectors.toSet());
 
+            // check semantics now and return
+            return configurationValidity(pseudowires);
+
         } catch (IllegalArgumentException e) {
             log.warn("{}", e.getMessage());
             return false;
         }
-
-        // check semantics now and return
-        return configurationValidity(pseudowires);
     }
 
     /**
@@ -204,9 +204,9 @@
                                                                             tunnelId)));
         }
 
-        if (((!ingressOuter.equals(VlanId.NONE) && !ingressOuter.equals(VlanId.NONE)) &&
+        if (((!ingressOuter.equals(VlanId.NONE) && !ingressInner.equals(VlanId.NONE)) &&
                 (egressOuter.equals(VlanId.NONE) && egressInner.equals(VlanId.NONE)))
-                || ((ingressOuter.equals(VlanId.NONE) && ingressOuter.equals(VlanId.NONE)) &&
+                || ((ingressOuter.equals(VlanId.NONE) && ingressInner.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.",
@@ -656,10 +656,9 @@
         newPw.put(MODE, mode);
 
         object.set(tunnelId, newPw);
-        try {
-            isValid();
-        } catch (IllegalArgumentException e) {
-            log.info("Pseudowire could not be created : {}", e);
+
+        if (!isValid()) {
+            log.info("Pseudowire could not be created : {}");
             object.remove(tunnelId);
             return null;
         }
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
index ab1363c..75cab32 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
@@ -247,6 +247,9 @@
         Set<DestinationSetNextObjectiveStoreKey> dsKeySet = dsNextObjStore.entrySet()
                 .stream()
                 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
+                // Filter out PW transit groups or include them if MPLS ECMP is supported
+                .filter(entry -> !entry.getKey().destinationSet().mplsSet() ||
+                        (entry.getKey().destinationSet().mplsSet() && srManager.getMplsEcmp()))
                 .filter(entry -> entry.getValue().containsNextHop(link.dst().deviceId()))
                 .map(entry -> entry.getKey())
                 .collect(Collectors.toSet());
@@ -325,7 +328,7 @@
      * a hashed group. User must ensure that all the ports & labels are meant
      * same neighbor (ie. dstMac).
      *
-     * @param portLables a collection of port & label combinations to add
+     * @param portLabels a collection of port & label combinations to add
      *                   to the hash group identified by the nextId
      * @param dstMac destination mac address of next-hop
      * @param nextId id for next-objective to which buckets will be added
@@ -376,7 +379,7 @@
      * a hash group. User must ensure that all the ports & labels are meant
      * same neighbor (ie. dstMac).
      *
-     * @param portLables a collection of port & label combinations to remove
+     * @param portLabels a collection of port & label combinations to remove
      *                   from the hash group identified by the nextId
      * @param dstMac destination mac address of next-hop
      * @param nextId id for next-objective from which buckets will be removed
@@ -1186,6 +1189,9 @@
             if (e.getValue().nextId() != objectiveId) {
                 continue;
             }
+            // Right now it is just used in TunnelHandler
+            // remember in future that PW transit groups could
+            // be Indirect groups
             NextObjective.Builder nextObjBuilder = DefaultNextObjective
                     .builder().withId(objectiveId)
                     .withType(NextObjective.Type.HASHED).fromApp(appId);
@@ -1344,6 +1350,9 @@
                 Set<DestinationSetNextObjectiveStoreKey> dsKeySet = dsNextObjStore.entrySet()
                         .stream()
                         .filter(entry -> entry.getKey().deviceId().equals(deviceId))
+                        // Filter out PW transit groups or include them if MPLS ECMP is supported
+                        .filter(entry -> !entry.getKey().destinationSet().mplsSet() ||
+                                (entry.getKey().destinationSet().mplsSet() && srManager.getMplsEcmp()))
                         .map(entry -> entry.getKey())
                         .collect(Collectors.toSet());
                 for (DestinationSetNextObjectiveStoreKey dsKey : dsKeySet) {
@@ -1412,4 +1421,4 @@
         }
     }
 
-}
\ No newline at end of file
+}
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelHandler.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelHandler.java
index 2675734..a2048b0 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelHandler.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelHandler.java
@@ -373,7 +373,7 @@
         // We terminate the tunnel
         result = deployPseudoWireTerm(pw.l2Tunnel(),
                                        pw.l2TunnelPolicy().cP2(),
-                                       pw.l2TunnelPolicy().cP2OuterTag(),
+                                       VlanId.NONE,
                                        FWD,
                                       spinePw);
 
@@ -414,7 +414,7 @@
 
         result = deployPseudoWireTerm(pw.l2Tunnel(),
                                        pw.l2TunnelPolicy().cP1(),
-                                       pw.l2TunnelPolicy().cP1OuterTag(),
+                                       VlanId.NONE,
                                        REV,
                                       spinePw);
 
@@ -725,7 +725,7 @@
                     return;
                 }
                 deployPseudoWireTerm(newPw.l2Tunnel(), newPw.l2TunnelPolicy().cP2(),
-                                      newPw.l2TunnelPolicy().cP2OuterTag(), FWD, finalNewPwSpine);
+                                      VlanId.NONE, FWD, finalNewPwSpine);
 
             }
         });
@@ -756,7 +756,7 @@
                 }
                 deployPseudoWireTerm(newPw.l2Tunnel(),
                                       newPw.l2TunnelPolicy().cP1(),
-                                      newPw.l2TunnelPolicy().cP1OuterTag(),
+                                      VlanId.NONE,
                                       REV, finalNewPwSpine);
             }
         });
diff --git a/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java b/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java
index cce5a31..e33a36d 100644
--- a/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java
+++ b/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java
@@ -212,6 +212,7 @@
         srManager.cfgService = mockNetworkConfigRegistry;
         mockLocationProbingService = new MockLocationProbingService();
         srManager.probingService = mockLocationProbingService;
+        srManager.linkHandler = new MockLinkHandler(srManager);
 
         hostHandler = new HostHandler(srManager);
 
diff --git a/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/MockLinkHandler.java b/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/MockLinkHandler.java
new file mode 100644
index 0000000..2819c9d
--- /dev/null
+++ b/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/MockLinkHandler.java
@@ -0,0 +1,36 @@
+/*
+ * 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;
+
+import org.onosproject.net.HostLocation;
+
+/**
+ * Mocks the LinkHandler in SR.
+ *
+ */
+public class MockLinkHandler extends LinkHandler {
+
+    MockLinkHandler(SegmentRoutingManager srManager) {
+        super(srManager, null);
+    }
+
+    @Override
+    void checkUplinksForDualHomedHosts(HostLocation loc) {
+        // currently does nothing - can be extended to be a useful mock when
+        // implementing unit tests for link handling
+    }
+}
diff --git a/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/PwaasConfigTest.java b/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/PwaasConfigTest.java
index 7686242..60163eb 100644
--- a/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/PwaasConfigTest.java
+++ b/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/PwaasConfigTest.java
@@ -303,9 +303,9 @@
         assertFalse(invalidConfigLabel.isValid());
     }
 
-    @Test(expected = IllegalArgumentException.class)
+    @Test
     public void testValid4() {
-        invalidConfigConflictingVlan.isValid();
+        assertFalse(invalidConfigConflictingVlan.isValid());
     }
 
     /**
diff --git a/apps/simplefabric/BUCK b/apps/simplefabric/BUCK
index 8125580..b215c46 100644
--- a/apps/simplefabric/BUCK
+++ b/apps/simplefabric/BUCK
@@ -2,6 +2,10 @@
     '//lib:CORE_DEPS',
     '//lib:JACKSON',
     '//lib:concurrent-trees',
+    '//lib:javax.ws.rs-api',
+    '//lib:org.apache.karaf.shell.console',
+    '//cli:onos-cli',
+    '//utils/rest:onlab-rest',
 ]
 
 BUNDLES = [
@@ -15,6 +19,7 @@
 osgi_jar_with_tests (
     deps = COMPILE_DEPS,
     test_deps = TEST_DEPS,
+    web_context = '/onos/v1/simplefabric',
 )
 
 onos_app (
diff --git a/apps/simplefabric/app.xml b/apps/simplefabric/app.xml
index df5cc60..1984e56 100644
--- a/apps/simplefabric/app.xml
+++ b/apps/simplefabric/app.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
-  ~ Copyright 2017-present Open Networking Laboratory
+  ~ 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.
diff --git a/apps/simplefabric/features.xml b/apps/simplefabric/features.xml
index 9ab23de..fd67ef1 100644
--- a/apps/simplefabric/features.xml
+++ b/apps/simplefabric/features.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <!--
-  ~ Copyright 2017-present Open Networking Laboratory
+  ~ 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.
diff --git a/apps/simplefabric/network-cfg.json b/apps/simplefabric/network-cfg.json
index f988497..c8e118b 100644
--- a/apps/simplefabric/network-cfg.json
+++ b/apps/simplefabric/network-cfg.json
@@ -38,8 +38,8 @@
     "org.onosproject.simplefabric" : {
       "simpleFabric" : {
         "l2Networks" : [
-          { "name" : "LEAF1", "interfaces" : ["h11", "h12", "h13", "h14", "d11", "d12" ], "l2Forward" : true },
-          { "name" : "LEAF2", "interfaces" : ["h21", "h22", "h23", "h24", "d21", "d22" ], "l2Forward" : true }
+          { "name" : "LEAF1", "interfaces" : ["h11", "h12", "h13", "h14", "d11", "d12" ], "l2Forward" : true, "l2Broadcast" : true },
+          { "name" : "LEAF2", "interfaces" : ["h21", "h22", "h23", "h24", "d21", "d22" ], "l2Forward" : true, "l2Broadcast" : true }
         ],
         "ipSubnets" : [
            { "ipPrefix" : "10.0.1.0/24", "gatewayIp" : "10.0.1.1", "gatewayMac" : "00:00:10:00:01:01", "l2NetworkName" : "LEAF1" },
diff --git a/apps/simplefabric/pom.xml b/apps/simplefabric/pom.xml
index 8954d24..e276ca7 100644
--- a/apps/simplefabric/pom.xml
+++ b/apps/simplefabric/pom.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
-  ~ Copyright 2017-present Open Networking Laboratory
+  ~ 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.
diff --git a/apps/simplefabric/rest-simplefabric b/apps/simplefabric/rest-simplefabric
new file mode 100755
index 0000000..130fa3b
--- /dev/null
+++ b/apps/simplefabric/rest-simplefabric
@@ -0,0 +1,62 @@
+#!/bin/bash
+
+#
+# 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.
+#
+
+# -----------------------------------------------------------------------------
+# Tool to manage ONOS applications using REST API.
+# -----------------------------------------------------------------------------
+
+# If ONOS_HOME is set, respect its value.
+# If ONOS_HOME is not set (e.g. in the init or service environment),
+# set it based on this script's path.
+ONOS_HOME=${ONOS_HOME:-$(cd $(dirname $0)/.. >/dev/null 2>&1 && pwd)}
+ONOS_WEB_USER=${ONOS_WEB_USER:-onos} # ONOS WEB User defaults to 'onos'
+ONOS_WEB_PASS=${ONOS_WEB_PASS:-rocks} # ONOS WEB Password defaults to 'rocks'
+
+. ${ONOS_HOME}/bin/_find-node
+
+node=$(find_node ${1:-localhost})
+cmd=${2:-show}
+
+export URL=http://$node:8181/onos/v1/simplefabric/$cmd
+#export HDR="-HContent-Type:application/octet-stream"
+#export HAJ="-HContent-Type:application/json"
+export curl="curl -sS --user $ONOS_WEB_USER:$ONOS_WEB_PASS --noproxy localhost "
+
+# Prints usage help
+function usage {
+    echo "usage: onos-simplefabric <node-ip> status|show|intents|reactive-intents|refresh|flush" >&2
+    exit 1
+}
+
+[ -z $node -o "$node" = "-h" -o "$node" = "--help" -o "$node" = "-?" ] && usage
+
+case $cmd in
+    status)  $curl -X GET $URL;;
+    show)    $curl -X GET $URL;;
+    intents) $curl -X GET $URL;;
+    reactive-intents) $curl -X GET $URL;;
+    refresh) $curl -X PUT $URL;;
+    flush)   $curl -X PUT $URL;;
+
+    *) usage;;
+esac
+
+
+status=$?
+echo # new line for prompt
+exit $status
diff --git a/apps/simplefabric/src/main/java/org/onosproject/simplefabric/L2Network.java b/apps/simplefabric/src/main/java/org/onosproject/simplefabric/L2Network.java
index 31e8cc0..5579159 100644
--- a/apps/simplefabric/src/main/java/org/onosproject/simplefabric/L2Network.java
+++ b/apps/simplefabric/src/main/java/org/onosproject/simplefabric/L2Network.java
@@ -41,6 +41,7 @@
     private Set<String> interfaceNames;   // also for network configuration
     private EncapsulationType encapsulation;  // also for network configuration
     private boolean l2Forward;            // do l2Forward (default:true) or not
+    private boolean l2Broadcast;          // do l2Broadcast (default:true) or not
 
     /* status variables */
     private Set<Interface> interfaces;    // available interfaces from interfaceNames
@@ -54,13 +55,16 @@
      * @param ifaceNames the interface names
      * @param encapsulation the encapsulation type
      * @param l2Forward flag for l2Forward intents to be installed or not
+     * @param l2Broadcast flag for l2Broadcast intents to be installed or not
      */
-    L2Network(String name, Collection<String> ifaceNames, EncapsulationType encapsulation, boolean l2Forward) {
+    L2Network(String name, Collection<String> ifaceNames, EncapsulationType encapsulation,
+              boolean l2Forward, boolean l2Broadcast) {
         this.name = name;
         this.interfaceNames = Sets.newHashSet();
         this.interfaceNames.addAll(ifaceNames);
         this.encapsulation = encapsulation;
         this.l2Forward = (SimpleFabricService.ALLOW_ETH_ADDRESS_SELECTOR) ? l2Forward : false;
+        this.l2Broadcast = (SimpleFabricService.ALLOW_ETH_ADDRESS_SELECTOR) ? l2Broadcast : false;
         this.interfaces = Sets.newHashSet();
         this.hostIds = Sets.newHashSet();
         this.dirty = false;
@@ -77,6 +81,7 @@
         this.interfaceNames = Sets.newHashSet();
         this.encapsulation = encapsulation;
         this.l2Forward = (SimpleFabricService.ALLOW_ETH_ADDRESS_SELECTOR) ? true : false;
+        this.l2Broadcast = (SimpleFabricService.ALLOW_ETH_ADDRESS_SELECTOR) ? true : false;
         this.interfaces = Sets.newHashSet();
         this.hostIds = Sets.newHashSet();
         this.dirty = false;
@@ -105,6 +110,7 @@
         L2Network l2NetworkCopy = new L2Network(l2Network.name(), l2Network.encapsulation());
         l2NetworkCopy.interfaceNames.addAll(l2Network.interfaceNames());
         l2NetworkCopy.l2Forward = (SimpleFabricService.ALLOW_ETH_ADDRESS_SELECTOR) ? l2Network.l2Forward() : false;
+        l2NetworkCopy.l2Broadcast = (SimpleFabricService.ALLOW_ETH_ADDRESS_SELECTOR) ? l2Network.l2Broadcast() : false;
         l2NetworkCopy.interfaces.addAll(l2Network.interfaces());
         l2NetworkCopy.hostIds.addAll(l2Network.hostIds());
         l2NetworkCopy.setDirty(l2Network.dirty());
@@ -150,6 +156,15 @@
     }
 
     /**
+     * Gets L2Network l2Broadcast flag.
+     *
+     * @return the l2Broadcast flag of L2Network
+     */
+    public boolean l2Broadcast() {
+        return l2Broadcast;
+    }
+
+    /**
      * Gets L2Network interfaces.
      *
      * @return the interfaces of L2Network
@@ -257,6 +272,7 @@
                 .add("interfaceNames", interfaceNames)
                 .add("encapsulation", encapsulation)
                 .add("l2Forward", l2Forward)
+                .add("l2Broadcast", l2Broadcast)
                 .add("interfaces", interfaces)
                 .add("hostIds", hostIds)
                 .add("dirty", dirty)
@@ -276,12 +292,13 @@
                && Objects.equals(other.interfaceNames, this.interfaceNames)
                && Objects.equals(other.encapsulation, this.encapsulation)
                && Objects.equals(other.l2Forward, this.l2Forward)
+               && Objects.equals(other.l2Broadcast, this.l2Broadcast)
                && Objects.equals(other.interfaces, this.interfaces)
                && Objects.equals(other.hostIds, this.hostIds);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(name, interfaces, encapsulation, l2Forward);
+        return Objects.hash(name, interfaces, encapsulation, l2Forward, l2Broadcast);
     }
 }
diff --git a/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricCommand.java b/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricCommand.java
new file mode 100644
index 0000000..e5ed60e
--- /dev/null
+++ b/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricCommand.java
@@ -0,0 +1,70 @@
+/*
+ * 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.simplefabric;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+
+/**
+ * CLI to interact with the SIMPLE_FABRIC application.
+ */
+@Command(scope = "onos", name = "simpleFabric",
+         description = "Manages the SimpleFabric application")
+public class SimpleFabricCommand extends AbstractShellCommand {
+
+    protected static SimpleFabricService simpleFabric;
+
+    @Argument(index = 0, name = "command",
+              description = "Command: show|intents|reactive-intents|refresh|flush",
+              required = true, multiValued = false)
+    String command = null;
+
+    @Override
+    protected void execute() {
+        if (simpleFabric == null) {
+            simpleFabric = get(SimpleFabricService.class);
+        }
+        if (command == null) {
+            print("command not found", command);
+            return;
+        }
+        switch (command) {
+        case "show":
+            simpleFabric.dumpToStream("show", System.out);
+            break;
+        case "intents":
+            simpleFabric.dumpToStream("intents", System.out);
+            break;
+        case "reactive-intents":
+            simpleFabric.dumpToStream("reactive-intents", System.out);
+            break;
+        case "refresh":
+            simpleFabric.triggerRefresh();
+            System.out.println("simple fabric refresh triggered");
+            break;
+        case "flush":
+            simpleFabric.triggerFlush();
+            System.out.println("simple fabric flush triggered");
+            break;
+        default:
+            System.out.println("unknown command: " + command);
+            break;
+        }
+    }
+
+}
+
diff --git a/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricCommandCompleter.java b/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricCommandCompleter.java
new file mode 100644
index 0000000..b810d73
--- /dev/null
+++ b/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricCommandCompleter.java
@@ -0,0 +1,52 @@
+/*
+ * 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.simplefabric;
+
+import com.google.common.collect.Lists;
+import org.apache.karaf.shell.console.completer.ArgumentCompleter;
+import org.onosproject.cli.AbstractChoicesCompleter;
+
+import java.util.List;
+import java.util.Arrays;
+import java.util.Collections;
+
+/**
+ * SimpleFabric command completer.
+ */
+public class SimpleFabricCommandCompleter extends AbstractChoicesCompleter {
+
+    public static final List<String> COMMAND_LIST =
+        Arrays.asList("show", "intents", "reactive-intents", "refresh", "flush");
+
+    @Override
+    public List<String> choices() {
+        ArgumentCompleter.ArgumentList argumentList = getArgumentList();
+        if (argumentList == null) {
+            return Collections.emptyList();
+        }
+        List<String> argList = Lists.newArrayList(argumentList.getArguments());
+        String argOne = null;
+        if (argList.size() > 1) {
+            argOne = argList.get(1);
+        }
+        if (COMMAND_LIST.contains(argOne)) {
+            return Collections.emptyList();
+        } else {
+            return COMMAND_LIST;
+        }
+    }
+}
+
diff --git a/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricConfig.java b/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricConfig.java
index a6d83b1..7bae152 100644
--- a/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricConfig.java
+++ b/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricConfig.java
@@ -40,6 +40,7 @@
     private static final String INTERFACES = "interfaces";
     private static final String ENCAPSULATION = "encapsulation";
     private static final String L2FORWARD = "l2Forward";
+    private static final String L2BROADCAST = "l2Broadcast";
     private static final String IPSUBNETS = "ipSubnets";
     private static final String BORDERROUTES = "borderRoutes";
     private static final String IPPREFIX = "ipPrefix";
@@ -78,12 +79,14 @@
             if (jsonNode.hasNonNull(L2FORWARD)) {
                 l2Forward = jsonNode.get(L2FORWARD).asBoolean();
             }
+            boolean l2Broadcast = true;
+            if (jsonNode.hasNonNull(L2BROADCAST)) {
+                l2Broadcast = jsonNode.get(L2BROADCAST).asBoolean();
+            }
             try {
                 l2Networks.add(new L2Network(
-                        jsonNode.get(NAME).asText(),
-                        ifaces,
-                        EncapsulationType.enumFromString(encapsulation),
-                        l2Forward));
+                        jsonNode.get(NAME).asText(), ifaces, EncapsulationType.enumFromString(encapsulation),
+                        l2Forward, l2Broadcast));
             } catch (Exception e) {
                 log.warn("simple fabric network config l2Network parse failed; skip: error={} jsonNode={}", jsonNode);
             }
diff --git a/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricL2Forward.java b/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricL2Forward.java
index 41f0253..a67ed1a 100644
--- a/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricL2Forward.java
+++ b/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricL2Forward.java
@@ -256,15 +256,15 @@
 
     private Set<Intent> generateL2NetworkIntents(L2Network l2Network) {
         return new ImmutableSet.Builder<Intent>()
-            .addAll(buildBrcIntents(l2Network))
-            .addAll(buildUniIntents(l2Network, hostsFromL2Network(l2Network)))
-            .build();
+                .addAll(buildBrcIntents(l2Network))
+                .addAll(buildUniIntents(l2Network, hostsFromL2Network(l2Network)))
+                .build();
     }
 
     // Build Boadcast Intents for a L2 Network.
     private Set<SinglePointToMultiPointIntent> buildBrcIntents(L2Network l2Network) {
         Set<Interface> interfaces = l2Network.interfaces();
-        if (!l2Network.l2Forward() || interfaces.size() < 2) {
+        if (interfaces.size() < 2 || !l2Network.l2Forward() || !l2Network.l2Broadcast()) {
             return ImmutableSet.of();
         }
         Set<SinglePointToMultiPointIntent> brcIntents = Sets.newHashSet();
@@ -384,7 +384,7 @@
 
     // Dump command handler
     private void dump(String subject, PrintStream out) {
-        if (subject == "intents") {
+        if ("intents".equals(subject)) {
             out.println("L2Forward Broadcast Intents:\n");
             for (SinglePointToMultiPointIntent intent: bctIntentsMap.values()) {
                 out.println("    " + intent.key().toString()
diff --git a/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricManager.java b/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricManager.java
index 2bb53c9..81dd602 100644
--- a/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricManager.java
+++ b/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricManager.java
@@ -71,7 +71,6 @@
 import java.io.OutputStream;
 import java.io.PrintStream;
 import java.nio.ByteBuffer;
-import java.util.Iterator;
 import java.util.HashSet;
 import java.util.Collection;
 import java.util.Set;
@@ -243,7 +242,7 @@
             for (Host host : hostService.getHosts()) {
                 // consider host with ip only
                 if (!host.ipAddresses().isEmpty()) {
-                    Interface iface = getAvailableDeviceHostInterface(host);
+                    Interface iface = findAvailableDeviceHostInterface(host);
                     if (iface != null && newL2Network.contains(iface)) {
                         newL2Network.addHost(host);
                     }
@@ -358,11 +357,6 @@
     }
 
     @Override
-    public MacAddress getVMacForIp(IpAddress ip) {
-        return virtualGatewayIpMacMap.get(ip);
-    }
-
-    @Override
     public boolean isVMac(MacAddress mac) {
         return virtualGatewayIpMacMap.containsValue(mac);
     }
@@ -373,6 +367,11 @@
     }
 
     @Override
+    public MacAddress findVMacForIp(IpAddress ip) {
+        return virtualGatewayIpMacMap.get(ip);
+    }
+
+    @Override
     public L2Network findL2Network(ConnectPoint port, VlanId vlanId) {
         for (L2Network l2Network : l2Networks) {
             if (l2Network.contains(port, vlanId)) {
@@ -394,34 +393,30 @@
 
     @Override
     public IpSubnet findIpSubnet(IpAddress ip) {
-        Iterator<IpSubnet> it;
         if (ip.isIp4()) {
-            it = ip4SubnetTable.getValuesForKeysPrefixing(
-                     createBinaryString(IpPrefix.valueOf(ip, Ip4Address.BIT_LENGTH))).iterator();
+            return ip4SubnetTable.getValueForLongestKeyPrefixing(
+                     createBinaryString(IpPrefix.valueOf(ip, Ip4Address.BIT_LENGTH)));
         } else {
-            it = ip6SubnetTable.getValuesForKeysPrefixing(
-                     createBinaryString(IpPrefix.valueOf(ip, Ip6Address.BIT_LENGTH))) .iterator();
+            return ip6SubnetTable.getValueForLongestKeyPrefixing(
+                     createBinaryString(IpPrefix.valueOf(ip, Ip6Address.BIT_LENGTH)));
         }
-        return (it.hasNext()) ? it.next() : null;
     }
 
     @Override
     public Route findBorderRoute(IpAddress ip) {
         // ASSUME: ipAddress is out of ipSubnet
-        Iterator<Route> it;
         if (ip.isIp4()) {
-            it = ip4BorderRouteTable.getValuesForKeysPrefixing(
-                     createBinaryString(IpPrefix.valueOf(ip, Ip4Address.BIT_LENGTH))) .iterator();
+            return ip4BorderRouteTable.getValueForLongestKeyPrefixing(
+                     createBinaryString(IpPrefix.valueOf(ip, Ip4Address.BIT_LENGTH)));
         } else {
-            it = ip6BorderRouteTable.getValuesForKeysPrefixing(
-                     createBinaryString(IpPrefix.valueOf(ip, Ip6Address.BIT_LENGTH))) .iterator();
+            return ip6BorderRouteTable.getValueForLongestKeyPrefixing(
+                     createBinaryString(IpPrefix.valueOf(ip, Ip6Address.BIT_LENGTH)));
         }
-        return (it.hasNext()) ? it.next() : null;
     }
 
 
     @Override
-    public Interface getHostInterface(Host host) {
+    public Interface findHostInterface(Host host) {
         return interfaceService.getInterfaces().stream()
                 .filter(iface -> iface.connectPoint().equals(host.location()) &&
                                  iface.vlan().equals(host.vlan()))
@@ -429,7 +424,7 @@
                 .orElse(null);
     }
 
-    private Interface getAvailableDeviceHostInterface(Host host) {
+    private Interface findAvailableDeviceHostInterface(Host host) {
         return interfaceService.getInterfaces().stream()
                 .filter(iface -> iface.connectPoint().equals(host.location()) &&
                                  iface.vlan().equals(host.vlan()))
@@ -439,29 +434,6 @@
     }
 
     @Override
-    public boolean isIpAddressLocal(IpAddress ip) {
-        boolean result;
-        if (ip.isIp4()) {
-            return ip4SubnetTable.getValuesForKeysPrefixing(
-                     createBinaryString(IpPrefix.valueOf(ip, Ip4Address.BIT_LENGTH)))
-                     .iterator().hasNext();
-        } else {
-            return ip6SubnetTable.getValuesForKeysPrefixing(
-                     createBinaryString(IpPrefix.valueOf(ip, Ip6Address.BIT_LENGTH)))
-                     .iterator().hasNext();
-        }
-    }
-
-    @Override
-    public boolean isIpPrefixLocal(IpPrefix ipPrefix) {
-        if (ipPrefix.isIp4()) {
-            return (ip4SubnetTable.getValueForExactKey(createBinaryString(ipPrefix)) != null);
-        } else {
-            return (ip6SubnetTable.getValueForExactKey(createBinaryString(ipPrefix)) != null);
-        }
-    }
-
-    @Override
     public boolean requestMac(IpAddress ip) {
         IpSubnet ipSubnet = findIpSubnet(ip);
         if (ipSubnet == null) {
@@ -510,8 +482,10 @@
 
     // Dump handler
     protected void dump(String subject, PrintStream out) {
-        if (subject == "show") {
+        if ("show".equals(subject)) {
             out.println("Static Configuration Flag:");
+            out.println("    ALLOW_IPV6="
+                        + SimpleFabricService.ALLOW_IPV6);
             out.println("    ALLOW_ETH_ADDRESS_SELECTOR="
                         + SimpleFabricService.ALLOW_ETH_ADDRESS_SELECTOR);
             out.println("    REACTIVE_SINGLE_TO_SINGLE="
diff --git a/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricNeighbour.java b/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricNeighbour.java
index 9d0b3fc..d796346 100644
--- a/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricNeighbour.java
+++ b/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricNeighbour.java
@@ -130,7 +130,7 @@
      * @param context the message context
      */
     protected void handleRequest(NeighbourMessageContext context) {
-        MacAddress mac = simpleFabric.getVMacForIp(context.target());
+        MacAddress mac = simpleFabric.findVMacForIp(context.target());
         if (mac != null) {
             log.trace("simple fabric neightbour request on virtualGatewayAddress {}; response to {} {} mac={}",
                       context.target(), context.inPort(), context.vlan(), mac);
@@ -181,7 +181,7 @@
         L2Network l2Network = simpleFabric.findL2Network(context.inPort(), context.vlan());
         if (l2Network != null) {
             // TODO: need to check and update simpleFabric.L2Network
-            MacAddress mac = simpleFabric.getVMacForIp(context.target());
+            MacAddress mac = simpleFabric.findVMacForIp(context.target());
             if (mac != null) {
                 log.trace("simple fabric neightbour response message to virtual gateway; drop: {} {} target={}",
                           context.inPort(), context.vlan(), context.target());
@@ -192,7 +192,7 @@
                 log.trace("simple fabric neightbour response message forward: {} {} target={} -> {}",
                           context.inPort(), context.vlan(), context.target(), hosts);
                 hosts.stream()
-                        .map(host -> simpleFabric.getHostInterface(host))
+                        .map(host -> simpleFabric.findHostInterface(host))
                         .filter(Objects::nonNull)
                         .forEach(context::forward);
             }
diff --git a/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricReactiveRouting.java b/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricReactiveRouting.java
index 10148c8..1a9d64f 100644
--- a/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricReactiveRouting.java
+++ b/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricReactiveRouting.java
@@ -60,6 +60,7 @@
 import org.onosproject.net.intent.IntentService;
 import org.onosproject.net.intent.Key;
 import org.onosproject.net.intent.MultiPointToSinglePointIntent;
+import org.onosproject.net.intf.Interface;
 import org.onosproject.net.link.LinkService;
 import org.onosproject.net.packet.DefaultOutboundPacket;
 import org.onosproject.net.packet.InboundPacket;
@@ -68,7 +69,6 @@
 import org.onosproject.net.packet.PacketPriority;
 import org.onosproject.net.packet.PacketProcessor;
 import org.onosproject.net.packet.PacketService;
-import org.onosproject.net.Port;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -226,7 +226,8 @@
                 // check if this devices has the ipSubnet, then add ip broadcast flue rule
                 L2Network l2Network = simpleFabric.findL2Network(subnet.l2NetworkName());
                 if (l2Network != null && l2Network.contains(device.id())) {
-                    newInterceptFlowRules.add(generateLocalSubnetIpBctFlowRule(device.id(), subnet.ipPrefix()));
+                    newInterceptFlowRules.add(generateLocalSubnetIpBctFlowRule(device.id(), subnet.ipPrefix(),
+                                                                               l2Network));
                 }
                 // JUST FOR FLOW RULE TEST ONLY
                 //newInterceptFlowRules.add(generateTestFlowRule(device.id(), subnet.ipPrefix()));
@@ -256,7 +257,7 @@
         }
     }
 
-    private FlowRule generateInterceptFlowRule(boolean isLocalSubnet, DeviceId deviceId, IpPrefix prefix) {
+    private FlowRule generateInterceptFlowRule(boolean isDstLocalSubnet, DeviceId deviceId, IpPrefix prefix) {
         TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
         if (prefix.isIp4()) {
             selector.matchEthType(Ethernet.TYPE_IPV4);
@@ -271,7 +272,7 @@
         }
         FlowRule rule = DefaultFlowRule.builder()
                 .forDevice(deviceId)
-                .withPriority(reactivePriority(false, isLocalSubnet, prefix.prefixLength()))
+                .withPriority(reactivePriority(false, isDstLocalSubnet, prefix.prefixLength()))
                 .withSelector(selector.build())
                 .withTreatment(DefaultTrafficTreatment.builder().punt().build())
                 .fromApp(reactiveAppId)
@@ -280,7 +281,7 @@
         return rule;
     }
 
-    private FlowRule generateLocalSubnetIpBctFlowRule(DeviceId deviceId, IpPrefix prefix) {
+    private FlowRule generateLocalSubnetIpBctFlowRule(DeviceId deviceId, IpPrefix prefix, L2Network l2Network) {
         TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
         IpPrefix bctPrefix;
         if (prefix.isIp4()) {
@@ -301,8 +302,10 @@
         }
         TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
         Set<ConnectPoint> newEgressPoints = new HashSet<>();
-        for (Port port : deviceService.getPorts(deviceId)) {
-            treatment.setOutput(port.number());
+        for (Interface iface : l2Network.interfaces()) {
+            if (iface.connectPoint().deviceId().equals(deviceId)) {
+                treatment.setOutput(iface.connectPoint().port());
+            }
         }
         FlowRule rule = DefaultFlowRule.builder()
                 .forDevice(deviceId)
@@ -548,8 +551,9 @@
     private boolean checkVirtualGatewayIpPacket(InboundPacket pkt, IpAddress srcIp, IpAddress dstIp) {
         Ethernet ethPkt = pkt.parsed();  // assume valid
 
-        MacAddress mac = simpleFabric.getVMacForIp(dstIp);
-        if (mac == null || !ethPkt.getDestinationMAC().equals(mac)) {
+        MacAddress mac = simpleFabric.findVMacForIp(dstIp);
+        if (mac == null || !simpleFabric.isVMac(ethPkt.getDestinationMAC())) {
+            /* Destination MAC should be any of virtual gateway macs */
             return false;
         } else if (dstIp.isIp4()) {
             IPv4 ipv4Packet = (IPv4) ethPkt.getPayload();
@@ -616,7 +620,6 @@
         IpAddress dstNextHop = dstIp;
         MacAddress treatmentSrcMac = ethPkt.getDestinationMAC();
         int borderRoutePrefixLength = 0;
-        boolean isLocalSubnet = true;
         boolean updateMac = simpleFabric.isVMac(ethPkt.getDestinationMAC());
 
         // check subnet local or route
@@ -624,25 +627,25 @@
         if (srcSubnet == null) {
             Route route = simpleFabric.findBorderRoute(srcIp);
             if (route == null) {
-                log.warn("route unknown: srcIp={}", srcIp);
+                log.warn("unknown srcIp; drop: srcCp={} srcIp={} dstIp={} ipProto={}",
+                         srcCp, srcIp, dstIp, ipProto);
                 return;
             }
             srcPrefix = route.prefix();
             srcNextHop = route.nextHop();
             borderRoutePrefixLength = route.prefix().prefixLength();
-            isLocalSubnet = false;
         }
         IpSubnet dstSubnet = simpleFabric.findIpSubnet(dstIp);
         if (dstSubnet == null) {
             Route route = simpleFabric.findBorderRoute(dstIp);
             if (route == null) {
-                log.warn("route unknown: dstIp={}", dstIp);
+                log.warn("unknown dstIp; drop: srcCp={} srcIp={} dstIp={} ipProto={}",
+                         srcCp, srcIp, dstIp, ipProto);
                 return;
             }
             dstPrefix = route.prefix();
             dstNextHop = route.nextHop();
             borderRoutePrefixLength = route.prefix().prefixLength();
-            isLocalSubnet = false;
         }
 
         if (dstSubnet != null) {
@@ -687,7 +690,7 @@
                  srcIp, dstIp, ethPkt.getSourceMAC(), ethPkt.getDestinationMAC(),
                  ethPkt.getVlanID(), ipProto, updateMac);
         setUpConnectivity(srcCp, ipProto, srcPrefix, dstPrefix, dstNextHop, treatmentSrcMac, encap, updateMac,
-                          isLocalSubnet, borderRoutePrefixLength);
+                          dstSubnet != null, borderRoutePrefixLength);
         forwardPacketToDstIp(context, dstNextHop, treatmentSrcMac, updateMac);
     }
 
@@ -737,18 +740,7 @@
     private boolean setUpConnectivity(ConnectPoint srcCp, byte ipProto, IpPrefix srcPrefix, IpPrefix dstPrefix,
                                       IpAddress nextHopIp, MacAddress treatmentSrcMac,
                                       EncapsulationType encap, boolean updateMac,
-                                      boolean isLocalSubnet, int borderRoutePrefixLength) {
-        Key key;
-        String keyProtoTag = "";
-        if (simpleFabric.REACTIVE_MATCH_IP_PROTO) {
-            keyProtoTag = "-p" + ipProto;
-        }
-        if (simpleFabric.REACTIVE_SINGLE_TO_SINGLE) {
-            key = Key.of(srcPrefix.toString() + "-to-" + dstPrefix.toString() + keyProtoTag, reactiveAppId);
-        } else {
-            key = Key.of(dstPrefix.toString() + keyProtoTag, reactiveAppId);
-        }
-
+                                      boolean isDstLocalSubnet, int borderRoutePrefixLength) {
         if (!(simpleFabric.findL2Network(srcCp, VlanId.NONE) != null ||
              (simpleFabric.REACTIVE_ALLOW_LINK_CP && !linkService.getIngressLinks(srcCp).isEmpty()))) {
             log.warn("NO REGI for srcCp not in L2Network; srcCp={} srcPrefix={} dstPrefix={} nextHopIp={}",
@@ -804,6 +796,19 @@
             }
         }
 
+        Key key;
+        String keyProtoTag = "";
+        if (simpleFabric.REACTIVE_MATCH_IP_PROTO) {
+            keyProtoTag = "-p" + ipProto;
+        }
+        if (simpleFabric.REACTIVE_SINGLE_TO_SINGLE) {
+            // allocate intent per (srcPrefix, dstPrefix)
+            key = Key.of(srcPrefix.toString() + "-to-" + dstPrefix.toString() + keyProtoTag, reactiveAppId);
+        } else {
+            // allocate intent per (srcDeviceId, dstPrefix)
+            key = Key.of(srcCp.deviceId().toString() + "-to-" +  dstPrefix.toString() + keyProtoTag, reactiveAppId);
+        }
+
         // check and merge already existing ingress points
         Set<ConnectPoint> ingressPoints = new HashSet<>();
         MultiPointToSinglePointIntent existingIntent = (MultiPointToSinglePointIntent) intentService.getIntent(key);
@@ -825,7 +830,7 @@
         }
 
         // priority for forwarding case
-        int priority = reactivePriority(true, isLocalSubnet, borderRoutePrefixLength);
+        int priority = reactivePriority(true, isDstLocalSubnet, borderRoutePrefixLength);
 
         MultiPointToSinglePointIntent newIntent = MultiPointToSinglePointIntent.builder()
             .key(key)
@@ -863,14 +868,14 @@
     }
 
     // priority calculator
-    private int reactivePriority(boolean isForward, boolean isLocalSubnet, int borderRoutePrefixLength) {
-        if (isLocalSubnet) {  // localSubnet <-> localSubnet
+    private int reactivePriority(boolean isForward, boolean isDstLocalSubnet, int borderRoutePrefixLength) {
+        if (isDstLocalSubnet) {  // -> dst:localSubnet
             if (isForward) {
                 return simpleFabric.PRI_REACTIVE_LOCAL_FORWARD;
             } else {  // isInterncept
                 return simpleFabric.PRI_REACTIVE_LOCAL_INTERCEPT;
             }
-        } else {  // isBorder: localSubnet <-> boarderRouteGateway
+        } else {  // -> dst:boarderRouteNextHop
             int offset;
             if (isForward) {
                 offset = simpleFabric.PRI_REACTIVE_BORDER_FORWARD;
@@ -897,7 +902,7 @@
 
     // Dump Cli Handler
     private void dump(String subject, PrintStream out) {
-        if (subject == "intents") {
+        if ("intents".equals(subject)) {
             out.println("Reactive Routing Route Intents:\n");
             for (Intent entry : intentService.getIntents()) {
                 if (reactiveAppId.equals(entry.appId())) {
@@ -931,7 +936,7 @@
             }
             out.println("");
 
-        } else if (subject == "reactive-intents") {
+        } else if ("reactive-intents".equals(subject)) {
             for (Intent entry : intentService.getIntents()) {
                 if (reactiveAppId.equals(entry.appId())) {
                     MultiPointToSinglePointIntent intent = (MultiPointToSinglePointIntent) entry;
diff --git a/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricService.java b/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricService.java
index c27a828..be2bed3 100644
--- a/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricService.java
+++ b/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricService.java
@@ -16,7 +16,6 @@
 package org.onosproject.simplefabric;
 
 import org.onlab.packet.IpAddress;
-import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 import org.onosproject.core.ApplicationId;
@@ -96,14 +95,6 @@
     Set<Route> getBorderRoutes();
 
     /**
-     * Gets Virtual Gateway Mac Address for Local Subnet Virtual Gateway Ip.
-     *
-     * @param ip the ip to check for Virtual Gateway Ip
-     * @return mac address of virtual gateway
-     */
-    MacAddress getVMacForIp(IpAddress ip);
-
-    /**
      * Evaluates whether a mac is of Virtual Gateway Mac Addresses.
      *
      * @param mac the MacAddress to evaluate
@@ -120,6 +111,14 @@
     boolean isL2NetworkInterface(Interface intf);
 
     /**
+     * Find Virtual Gateway Mac Address for Local Subnet Virtual Gateway Ip.
+     *
+     * @param ip the ip to check for Virtual Gateway Ip
+     * @return mac address of virtual gateway
+     */
+    MacAddress findVMacForIp(IpAddress ip);
+
+    /**
      * Finds the L2 Network with given port and vlanId.
      *
      * @param port the port to be matched
@@ -159,23 +158,7 @@
      * @param host the host
      * @return the interface related to the host
      */
-    Interface getHostInterface(Host host);
-
-    /**
-     * Evaluates whether an IP address belongs to local SDN network.
-     *
-     * @param ipAddress the IP address to evaluate
-     * @return true if the IP address belongs to local SDN network, otherwise false
-     */
-    boolean isIpAddressLocal(IpAddress ipAddress);
-
-    /**
-     * Evaluates whether an IP prefix belongs to local SDN network.
-     *
-     * @param ipPrefix the IP prefix to evaluate
-     * @return true if the IP prefix belongs to local SDN network, otherwise false
-     */
-    boolean isIpPrefixLocal(IpPrefix ipPrefix);
+    Interface findHostInterface(Host host);
 
     /**
      * Sends Neighbour Query (ARP or NDP) to Find Host Location.
diff --git a/apps/restconf/utils/src/main/java/org/onosproject/restconf/utils/exceptions/package-info.java b/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricWebApplication.java
similarity index 63%
copy from apps/restconf/utils/src/main/java/org/onosproject/restconf/utils/exceptions/package-info.java
copy to apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricWebApplication.java
index a0fe88e..d82de73 100644
--- a/apps/restconf/utils/src/main/java/org/onosproject/restconf/utils/exceptions/package-info.java
+++ b/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricWebApplication.java
@@ -14,7 +14,19 @@
  * limitations under the License.
  */
 
+package org.onosproject.simplefabric;
+
+import org.onlab.rest.AbstractWebApplication;
+
+import java.util.Set;
+
 /**
- * Parse utils custom exceptions.
+ * SIMPLE_FABRIC REST API web application.
  */
-package org.onosproject.restconf.utils.exceptions;
\ No newline at end of file
+public class SimpleFabricWebApplication extends AbstractWebApplication {
+    @Override
+    public Set<Class<?>> getClasses() {
+        return getClasses(SimpleFabricWebResource.class);
+    }
+}
+
diff --git a/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricWebResource.java b/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricWebResource.java
new file mode 100644
index 0000000..f1e9af7
--- /dev/null
+++ b/apps/simplefabric/src/main/java/org/onosproject/simplefabric/SimpleFabricWebResource.java
@@ -0,0 +1,108 @@
+/*
+ * 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.simplefabric;
+
+import org.onosproject.rest.AbstractWebResource;
+
+import java.io.ByteArrayOutputStream;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Response;
+
+/**
+ * Manage SIMPLE_FABRIC Status.
+ */
+@Path("")
+public class SimpleFabricWebResource extends AbstractWebResource {
+
+    /**
+     * SIMPLE_FABRIC Show Status; dummy for now.
+     *
+     * @return 200 OK
+     */
+    @GET
+    @Path("status")
+    public Response queryStatus() {
+        return Response.ok("ok").build();
+    }
+
+    /**
+     * SIMPLE_FABRIC Show Configurations.
+     *
+     * @return 200 OK
+     */
+    @GET
+    @Path("show")
+    public Response queryShow() {
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        get(SimpleFabricService.class).dumpToStream("show", outputStream);
+        return Response.ok(outputStream.toString()).build();
+    }
+
+    /**
+     * SIMPLE_FABRIC Intents Infos.
+     *
+     * @return 200 OK
+     */
+    @GET
+    @Path("intents")
+    public Response queryIntents() {
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        get(SimpleFabricService.class).dumpToStream("intents", outputStream);
+        return Response.ok(outputStream.toString()).build();
+    }
+
+    /**
+     * SIMPLE_FABRIC Reactive Intents Infos.
+     *
+     * @return 200 OK
+     */
+    @GET
+    @Path("reactive-intents")
+    public Response queryReactiveIntents() {
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        get(SimpleFabricService.class).dumpToStream("reactive-intents", outputStream);
+        return Response.ok(outputStream.toString()).build();
+    }
+
+    /**
+     * Trigger SimpleFabric Service Refresh.
+     *
+     * @return 204 No Content
+     */
+    @PUT
+    @Path("refresh")
+    public Response triggerRefresh() {
+        get(SimpleFabricService.class).triggerRefresh();
+        return Response.status(204).build();
+    }
+
+    /**
+     * Trigger SimpleFabric Service Flush Reactive Intents.
+     *
+     * @return 204 No Content
+     */
+    @PUT
+    @Path("flush")
+    public Response triggerFlush() {
+        get(SimpleFabricService.class).triggerFlush();
+        return Response.status(204).build();
+    }
+
+}
+
diff --git a/apps/simplefabric/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/simplefabric/src/main/resources/OSGI-INF/blueprint/shell-config.xml
new file mode 100644
index 0000000..7b6893b
--- /dev/null
+++ b/apps/simplefabric/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -0,0 +1,28 @@
+<!--
+  ~ 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.
+  -->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+
+    <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+        <command>
+            <action class="org.onosproject.simplefabric.SimpleFabricCommand"/>
+            <completers>
+                <ref component-id="SimpleFabricCommandCompleter"/>
+            </completers>
+        </command>
+    </command-bundle>
+ 
+    <bean id="SimpleFabricCommandCompleter" class="org.onosproject.simplefabric.SimpleFabricCommandCompleter"/>
+</blueprint>
diff --git a/apps/simplefabric/src/main/webapp/WEB-INF/web.xml b/apps/simplefabric/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..a0e0004
--- /dev/null
+++ b/apps/simplefabric/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
+         xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+         id="ONOS" version="2.5">
+    <display-name>Simple Fabric application REST API</display-name>
+
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>Secured</web-resource-name>
+            <url-pattern>/*</url-pattern>
+        </web-resource-collection>
+        <auth-constraint>
+            <role-name>admin</role-name>
+        </auth-constraint>
+    </security-constraint>
+
+    <security-role>
+        <role-name>admin</role-name>
+    </security-role>
+
+    <login-config>
+        <auth-method>BASIC</auth-method>
+        <realm-name>karaf</realm-name>
+    </login-config>
+
+    <servlet>
+        <servlet-name>JAX-RS Service</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.onosproject.simplefabric.SimpleFabricWebApplication</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>JAX-RS Service</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+
+</web-app>
diff --git a/apps/t3/BUCK b/apps/t3/BUCK
index bca18be..a21999d 100644
--- a/apps/t3/BUCK
+++ b/apps/t3/BUCK
@@ -6,16 +6,23 @@
     '//core/api:onos-api',
     '//lib:org.apache.karaf.shell.console',
     '//cli:onos-cli',
+    '//drivers/default:onos-drivers-default',
+]
+
+TEST_DEPS = [
+    '//lib:TEST_ADAPTERS',
+    '//utils/misc:onlab-misc',
 ]
 
 osgi_jar_with_tests (
     deps = COMPILE_DEPS,
+    test_deps = TEST_DEPS,
 )
 
 onos_app (
     title = 'Trellis Troubleshooting Toolkit',
-    category = 'Utility',
-    url = 'http://onosproject.org',
+    category = 'Utilities',
+    url = 'https://wiki.opencord.org/pages/viewpage.action?pageId=4456974',
     description = 'Provides static analysis of flows and groups ' +
     'to determine the possible paths a packet may take.',
 )
diff --git a/apps/t3/pom.xml b/apps/t3/pom.xml
index ef1349e..e82a900 100644
--- a/apps/t3/pom.xml
+++ b/apps/t3/pom.xml
@@ -54,6 +54,12 @@
         </dependency>
         <dependency>
             <groupId>org.onosproject</groupId>
+            <artifactId>onos-api</artifactId>
+            <scope>test</scope>
+            <classifier>tests</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
             <artifactId>onos-core-primitives</artifactId>
             <version>${project.version}</version>
         </dependency>
@@ -70,6 +76,16 @@
             <groupId>org.apache.felix</groupId>
             <artifactId>org.apache.felix.scr.annotations</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-drivers-default</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-misc</artifactId>
+            <version>${project.version}</version>
+        </dependency>
     </dependencies>
 
 </project>
diff --git a/apps/t3/src/main/java/org/onosproject/t3/api/GroupsInDevice.java b/apps/t3/src/main/java/org/onosproject/t3/api/GroupsInDevice.java
new file mode 100644
index 0000000..3cfb41a
--- /dev/null
+++ b/apps/t3/src/main/java/org/onosproject/t3/api/GroupsInDevice.java
@@ -0,0 +1,81 @@
+/*
+ * 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.t3.api;
+
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.group.Group;
+
+import java.util.List;
+
+/**
+ * Class to represent the groups in a device for a given output and packet.
+ */
+//FIXME consider removing.
+public class GroupsInDevice {
+
+    private ConnectPoint output;
+    private List<Group> groups;
+    private TrafficSelector selector;
+
+    /**
+     * Saves the given groups for the output connect point and the selector.
+     * @param output the output connect point
+     * @param groups the groups
+     * @param selector the selector representing the final packet
+     */
+    public GroupsInDevice(ConnectPoint output, List<Group> groups, TrafficSelector selector) {
+
+        this.output = output;
+        this.groups = groups;
+        this.selector = selector;
+    }
+
+    /**
+     * Returns the output connect point.
+     * @return the connect point
+     */
+    public ConnectPoint getOutput() {
+        return output;
+    }
+
+    /**
+     * Returns the groups.
+     * @return groups.
+     */
+    public List<Group> getGroups() {
+        return groups;
+    }
+
+    /**
+     * Returns the final packet after traversing the network.
+     * @return the selector with packet info
+     */
+    public TrafficSelector getFinalPacket() {
+        return selector;
+    }
+
+    @Override
+    public String toString() {
+        return "GroupsInDevice{" +
+                "output=" + output +
+                ", groups=" + groups +
+                ", selector=" + selector +
+                '}';
+    }
+
+}
diff --git a/apps/t3/src/main/java/org/onosproject/t3/api/StaticPacketTrace.java b/apps/t3/src/main/java/org/onosproject/t3/api/StaticPacketTrace.java
index e6ee8e8..e56035d 100644
--- a/apps/t3/src/main/java/org/onosproject/t3/api/StaticPacketTrace.java
+++ b/apps/t3/src/main/java/org/onosproject/t3/api/StaticPacketTrace.java
@@ -16,10 +16,154 @@
 
 package org.onosproject.t3.api;
 
+import com.google.common.collect.ImmutableList;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.TrafficSelector;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 /**
  * Encapsulates the result of tracing a packet (traffic selector) through
  * the current topology.
  */
 public class StaticPacketTrace {
 
+    private final TrafficSelector inPacket;
+    private final ConnectPoint in;
+    List<List<ConnectPoint>> completePaths;
+    private Map<DeviceId, List<GroupsInDevice>> outputsForDevice;
+    private Map<DeviceId, List<FlowEntry>> flowsForDevice;
+    private StringBuilder resultMessage;
+
+    /**
+     * Builds the trace with a given packet and a connect point.
+     *
+     * @param packet the packet to trace
+     * @param in     the initial connect point
+     */
+    public StaticPacketTrace(TrafficSelector packet, ConnectPoint in) {
+        this.inPacket = packet;
+        this.in = in;
+        completePaths = new ArrayList<>();
+        outputsForDevice = new HashMap<>();
+        flowsForDevice = new HashMap<>();
+        resultMessage = new StringBuilder();
+    }
+
+    /**
+     * Return the initial packet.
+     *
+     * @return the initial packet in the form of a selector.
+     */
+    public TrafficSelector getInitialPacket() {
+        return inPacket;
+    }
+
+    /**
+     * Returns the first connect point the packet came in through.
+     *
+     * @return the connect point
+     */
+    public ConnectPoint getInitialConnectPoint() {
+        return in;
+    }
+
+    /**
+     * Add a result message for the Trace.
+     *
+     * @param resultMessage the message
+     */
+    public void addResultMessage(String resultMessage) {
+        if (this.resultMessage.length() != 0) {
+            this.resultMessage.append("\n");
+        }
+        this.resultMessage.append(resultMessage);
+    }
+
+    /**
+     * Return the result message.
+     *
+     * @return the message
+     */
+    public String resultMessage() {
+        return resultMessage.toString();
+    }
+
+    /**
+     * Adds the groups for a given device.
+     *
+     * @param deviceId   the device
+     * @param outputPath the groups in device objects
+     */
+    public void addGroupOutputPath(DeviceId deviceId, GroupsInDevice outputPath) {
+        if (!outputsForDevice.containsKey(deviceId)) {
+            outputsForDevice.put(deviceId, new ArrayList<>());
+        }
+        outputsForDevice.get(deviceId).add(outputPath);
+    }
+
+    /**
+     * Returns all the possible group-based outputs for a given device.
+     *
+     * @param deviceId the device
+     * @return the list of Groups for this device.
+     */
+    public List<GroupsInDevice> getGroupOuputs(DeviceId deviceId) {
+        return outputsForDevice.get(deviceId);
+    }
+
+    /**
+     * Adds a complete possible path.
+     *
+     * @param completePath the path
+     */
+    public void addCompletePath(List<ConnectPoint> completePath) {
+        completePaths.add(completePath);
+    }
+
+    /**
+     * Return all the possible path the packet can take through the network.
+     *
+     * @return a list of paths
+     */
+    public List<List<ConnectPoint>> getCompletePaths() {
+        return completePaths;
+    }
+
+    /**
+     * Add the flows traversed by the packet in a given device.
+     *
+     * @param deviceId the device considered
+     * @param flows    the flows
+     */
+    public void addFlowsForDevice(DeviceId deviceId, List<FlowEntry> flows) {
+        flowsForDevice.put(deviceId, flows);
+    }
+
+    /**
+     * Returns the flows matched by this trace's packet for a given device.
+     *
+     * @param deviceId the device
+     * @return the flows matched
+     */
+    public List<FlowEntry> getFlowsForDevice(DeviceId deviceId) {
+        return flowsForDevice.getOrDefault(deviceId, ImmutableList.of());
+    }
+
+    @Override
+    public String toString() {
+        return "StaticPacketTrace{" +
+                "inPacket=" + inPacket +
+                ", in=" + in +
+                ", completePaths=" + completePaths +
+                ", outputsForDevice=" + outputsForDevice +
+                ", flowsForDevice=" + flowsForDevice +
+                ", resultMessage=" + resultMessage +
+                '}';
+    }
 }
diff --git a/apps/t3/src/main/java/org/onosproject/t3/cli/TroubleshootTraceCommand.java b/apps/t3/src/main/java/org/onosproject/t3/cli/TroubleshootTraceCommand.java
new file mode 100644
index 0000000..c809065
--- /dev/null
+++ b/apps/t3/src/main/java/org/onosproject/t3/cli/TroubleshootTraceCommand.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright 2015-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.t3.cli;
+
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.MplsLabel;
+import org.onlab.packet.TpPort;
+import org.onlab.packet.VlanId;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.t3.api.StaticPacketTrace;
+import org.onosproject.t3.api.TroubleshootService;
+
+import java.util.List;
+
+import static org.onlab.packet.EthType.EtherType;
+
+/**
+ * Starts a Static Packet Trace for a given input and prints the result.
+ */
+@Command(scope = "onos", name = "troubleshoot",
+        description = "troubleshoots flows and groups between source and destination")
+public class TroubleshootTraceCommand extends AbstractShellCommand {
+
+
+    private static final String FLOW_SHORT_FORMAT = "    %s, bytes=%s, packets=%s, "
+            + "table=%s, priority=%s, selector=%s, treatment=%s";
+
+    private static final String GROUP_FORMAT =
+            "   id=0x%s, state=%s, type=%s, bytes=%s, packets=%s, appId=%s, referenceCount=%s";
+    private static final String GROUP_BUCKET_FORMAT =
+            "       id=0x%s, bucket=%s, bytes=%s, packets=%s, actions=%s";
+
+    @Option(name = "-v", aliases = "--verbose", description = "Outputs complete path")
+    private boolean verbosity1 = false;
+
+    @Option(name = "-vv", aliases = "--veryverbose", description = "Outputs flows and groups for every device")
+    private boolean verbosity2 = false;
+
+    @Option(name = "-s", aliases = "--srcIp", description = "Source IP")
+    String srcIp = null;
+
+    @Option(name = "-sp", aliases = "--srcPort", description = "Source Port", required = true)
+    String srcPort = null;
+
+    @Option(name = "-sm", aliases = "--srcMac", description = "Source MAC")
+    String srcMac = null;
+
+    @Option(name = "-et", aliases = "--ethType", description = "ETH Type", valueToShowInHelp = "ipv4")
+    String ethType = "ipv4";
+
+    @Option(name = "-stp", aliases = "--srcTcpPort", description = "Source TCP Port")
+    String srcTcpPort = null;
+
+    @Option(name = "-d", aliases = "--dstIp", description = "Destination IP")
+    String dstIp = null;
+
+    @Option(name = "-dm", aliases = "--dstMac", description = "Destination MAC")
+    String dstMac = null;
+
+    @Option(name = "-dtp", aliases = "dstTcpPort", description = "destination TCP Port")
+    String dstTcpPort = null;
+
+    @Option(name = "-vid", aliases = "--vlanId", description = "Vlan of incoming packet", valueToShowInHelp = "None")
+    String vlan = "None";
+
+    @Option(name = "-ml", aliases = "--mplsLabel", description = "Mpls label of incoming packet")
+    String mplsLabel = null;
+
+    @Option(name = "-mb", aliases = "--mplsBos", description = "MPLS BOS", valueToShowInHelp = "True")
+    String mplsBos = null;
+
+    @Override
+    protected void execute() {
+        TroubleshootService service = get(TroubleshootService.class);
+        ConnectPoint cp = ConnectPoint.deviceConnectPoint(srcPort);
+        EtherType type = EtherType.valueOf(ethType.toUpperCase());
+
+        //Input Port must be specified
+        TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
+                .matchInPort(cp.port());
+
+        if (srcIp != null) {
+            if (type.equals(EtherType.IPV6)) {
+                selectorBuilder.matchIPv6Src(IpAddress.valueOf(srcIp).toIpPrefix());
+            } else {
+                selectorBuilder.matchIPSrc(IpAddress.valueOf(srcIp).toIpPrefix());
+            }
+        }
+
+        if (srcMac != null) {
+            selectorBuilder.matchEthSrc(MacAddress.valueOf(srcMac));
+        }
+
+        //if EthType option is not specified using IPv4
+        selectorBuilder.matchEthType(type.ethType().toShort());
+
+        if (srcTcpPort != null) {
+            selectorBuilder.matchTcpSrc(TpPort.tpPort(Integer.parseInt(srcTcpPort)));
+        }
+
+        if (dstIp != null) {
+            if (type.equals(EtherType.IPV6)) {
+                selectorBuilder.matchIPv6Dst(IpAddress.valueOf(dstIp).toIpPrefix());
+            } else {
+                selectorBuilder.matchIPDst(IpAddress.valueOf(dstIp).toIpPrefix());
+            }
+        }
+
+        if (dstMac != null) {
+            selectorBuilder.matchEthDst(MacAddress.valueOf(dstMac));
+        }
+        if (dstTcpPort != null) {
+            selectorBuilder.matchTcpDst(TpPort.tpPort(Integer.parseInt(dstTcpPort)));
+        }
+
+        //if vlan option is not specified using NONE
+        selectorBuilder.matchVlanId(VlanId.vlanId(vlan));
+
+        if (mplsLabel != null) {
+            selectorBuilder.matchMplsLabel(MplsLabel.mplsLabel(Integer.parseInt(mplsLabel)));
+        }
+
+        if (mplsBos != null) {
+            selectorBuilder.matchMplsBos(Boolean.valueOf(mplsBos));
+        }
+
+        TrafficSelector packet = selectorBuilder.build();
+
+        //Printing the created packet
+        print("Tracing packet: %s", packet.criteria());
+
+        //Build the trace
+        StaticPacketTrace trace = service.trace(packet, cp);
+
+        //Print based on verbosity
+        if (verbosity1) {
+            printTrace(trace, false);
+        } else if (verbosity2) {
+            printTrace(trace, true);
+        } else {
+            print("Paths");
+            List<List<ConnectPoint>> paths = trace.getCompletePaths();
+            paths.forEach(path -> print("%s", path));
+        }
+        print("Result: \n%s", trace.resultMessage());
+    }
+
+    //prints the trace
+    private void printTrace(StaticPacketTrace trace, boolean verbose) {
+        List<List<ConnectPoint>> paths = trace.getCompletePaths();
+        paths.forEach(path -> {
+            print("Path %s", path);
+            ConnectPoint previous = null;
+            for (ConnectPoint connectPoint : path) {
+                if (previous == null || !previous.deviceId().equals(connectPoint.deviceId())) {
+                    print("Device %s", connectPoint.deviceId());
+                    print("Input from %s", connectPoint);
+                    printFlows(trace, verbose, connectPoint);
+                } else {
+                    printGroups(trace, verbose, connectPoint);
+                    print("Output through %s", connectPoint);
+                    print("");
+                }
+                previous = connectPoint;
+            }
+        });
+    }
+
+    //Prints the flows for a given trace and a specified level of verbosity
+    private void printFlows(StaticPacketTrace trace, boolean verbose, ConnectPoint connectPoint) {
+        print("Flows");
+        trace.getFlowsForDevice(connectPoint.deviceId()).forEach(f -> {
+            if (verbose) {
+                print(FLOW_SHORT_FORMAT, f.state(), f.bytes(), f.packets(),
+                        f.table(), f.priority(), f.selector().criteria(),
+                        printTreatment(f.treatment()));
+            } else {
+                print("   flowId=%s, selector=%s ", f.id(), f.selector().criteria());
+            }
+        });
+    }
+
+    //Prints the groups for a given trace and a specified level of verbosity
+    private void printGroups(StaticPacketTrace trace, boolean verbose, ConnectPoint connectPoint) {
+        print("Groups");
+        trace.getGroupOuputs(connectPoint.deviceId()).forEach(output -> {
+            if (output.getOutput().equals(connectPoint)) {
+                output.getGroups().forEach(group -> {
+                    if (verbose) {
+                        print(GROUP_FORMAT, Integer.toHexString(group.id().id()), group.state(), group.type(),
+                                group.bytes(), group.packets(), group.appId().name(), group.referenceCount());
+                        int i = 0;
+                        for (GroupBucket bucket : group.buckets().buckets()) {
+                            print(GROUP_BUCKET_FORMAT, Integer.toHexString(group.id().id()), ++i,
+                                    bucket.bytes(), bucket.packets(),
+                                    bucket.treatment().allInstructions());
+                        }
+                    } else {
+                        print("   groupId=%s", group.id());
+                    }
+                });
+                print("Outgoing Packet %s", output.getFinalPacket());
+            }
+        });
+    }
+
+    private String printTreatment(TrafficTreatment treatment) {
+        final String delimiter = ", ";
+        StringBuilder builder = new StringBuilder("[");
+        if (!treatment.immediate().isEmpty()) {
+            builder.append("immediate=" + treatment.immediate() + delimiter);
+        }
+        if (!treatment.deferred().isEmpty()) {
+            builder.append("deferred=" + treatment.deferred() + delimiter);
+        }
+        if (treatment.clearedDeferred()) {
+            builder.append("clearDeferred" + delimiter);
+        }
+        if (treatment.tableTransition() != null) {
+            builder.append("transition=" + treatment.tableTransition() + delimiter);
+        }
+        if (treatment.metered() != null) {
+            builder.append("meter=" + treatment.metered() + delimiter);
+        }
+        if (treatment.writeMetadata() != null) {
+            builder.append("metadata=" + treatment.writeMetadata() + delimiter);
+        }
+        // Chop off last delimiter
+        builder.replace(builder.length() - delimiter.length(), builder.length(), "");
+        builder.append("]");
+        return builder.toString();
+    }
+}
diff --git a/apps/t3/src/main/java/org/onosproject/t3/impl/Subnet.java b/apps/t3/src/main/java/org/onosproject/t3/impl/Subnet.java
new file mode 100644
index 0000000..ac5a741
--- /dev/null
+++ b/apps/t3/src/main/java/org/onosproject/t3/impl/Subnet.java
@@ -0,0 +1,133 @@
+/*
+ * 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.t3.impl;
+
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * Utility class to test if an Ip is in a given subnet.
+ */
+public class Subnet {
+    private final int bytesSubnetCount;
+    private final BigInteger bigMask;
+    private final BigInteger bigSubnetMasked;
+
+    /**
+     * Constructor for use via format "192.168.0.0/24" or "2001:db8:85a3:880:0:0:0:0/57".
+     * @param subnetAddress the address
+     * @param bits the mask
+     */
+    public Subnet(InetAddress subnetAddress, int bits) {
+        bytesSubnetCount = subnetAddress.getAddress().length; // 4 or 16
+        bigMask = BigInteger.valueOf(-1).shiftLeft(bytesSubnetCount * 8 - bits); // mask = -1 << 32 - bits
+        bigSubnetMasked = new BigInteger(subnetAddress.getAddress()).and(bigMask);
+    }
+
+    /**
+     * Constructor for use via format "192.168.0.0/255.255.255.0" or single address.
+     * @param subnetAddress the address
+     * @param mask the mask
+     */
+    public Subnet(InetAddress subnetAddress, InetAddress mask) {
+        bytesSubnetCount = subnetAddress.getAddress().length;
+        // no mask given case is handled here.
+        bigMask = null == mask ? BigInteger.valueOf(-1) : new BigInteger(mask.getAddress());
+        bigSubnetMasked = new BigInteger(subnetAddress.getAddress()).and(bigMask);
+    }
+
+    /**
+     * Subnet factory method.
+     *
+     * @param subnetMask format: "192.168.0.0/24" or "192.168.0.0/255.255.255.0"
+     *                   or single address or "2001:db8:85a3:880:0:0:0:0/57"
+     * @return a new instance
+     * @throws UnknownHostException thrown if unsupported subnet mask.
+     */
+    public static Subnet createInstance(String subnetMask)
+            throws UnknownHostException {
+        final String[] stringArr = subnetMask.split("/");
+        if (2 > stringArr.length) {
+            return new Subnet(InetAddress.getByName(stringArr[0]), (InetAddress) null);
+        } else if (stringArr[1].contains(".") || stringArr[1].contains(":")) {
+            return new Subnet(InetAddress.getByName(stringArr[0]), InetAddress.getByName(stringArr[1]));
+        } else {
+            return new Subnet(InetAddress.getByName(stringArr[0]), Integer.parseInt(stringArr[1]));
+        }
+    }
+
+    /**
+     * Tests if the address is in the given subnet.
+     * @param address the address to test.
+     * @return true if inside the subnet
+     */
+    public boolean isInSubnet(InetAddress address) {
+        byte[] bytesAddress = address.getAddress();
+        if (this.bytesSubnetCount != bytesAddress.length) {
+            return false;
+        }
+        BigInteger bigAddress = new BigInteger(bytesAddress);
+        return bigAddress.and(this.bigMask).equals(this.bigSubnetMasked);
+    }
+
+    @Override
+    public final boolean equals(Object obj) {
+        if (!(obj instanceof Subnet)) {
+            return false;
+        }
+        final Subnet other = (Subnet) obj;
+        return bigSubnetMasked.equals(other.bigSubnetMasked) &&
+                bigMask.equals(other.bigMask) &&
+                bytesSubnetCount == other.bytesSubnetCount;
+    }
+
+    @Override
+    public final int hashCode() {
+        return bytesSubnetCount;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder buf = new StringBuilder();
+        bigInteger2IpString(buf, bigSubnetMasked, bytesSubnetCount);
+        buf.append('/');
+        bigInteger2IpString(buf, bigMask, bytesSubnetCount);
+        return buf.toString();
+    }
+
+    private void bigInteger2IpString(StringBuilder buf, BigInteger bigInteger, int displayBytes) {
+        boolean isIPv4 = 4 == displayBytes;
+        byte[] bytes = bigInteger.toByteArray();
+        int diffLen = displayBytes - bytes.length;
+        byte fillByte = 0 > (int) bytes[0] ? (byte) 0xFF : (byte) 0x00;
+
+        int integer;
+        for (int i = 0; i < displayBytes; i++) {
+            if (0 < i && !isIPv4 && i % 2 == 0) {
+                buf.append(':');
+            } else if (0 < i && isIPv4) {
+                buf.append('.');
+            }
+            integer = 0xFF & (i < diffLen ? fillByte : bytes[i - diffLen]);
+            if (!isIPv4 && 0x10 > integer) {
+                buf.append('0');
+            }
+            buf.append(isIPv4 ? integer : Integer.toHexString(integer));
+        }
+    }
+}
diff --git a/apps/t3/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java b/apps/t3/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java
index 03f1e75..b66996d 100644
--- a/apps/t3/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java
+++ b/apps/t3/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java
@@ -16,22 +16,64 @@
 
 package org.onosproject.t3.impl;
 
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
+import org.onlab.packet.VlanId;
 import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.Link;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.DriverService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.IndexTableId;
+import org.onosproject.net.flow.TableId;
 import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.criteria.Criteria;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.EthCriterion;
+import org.onosproject.net.flow.criteria.EthTypeCriterion;
+import org.onosproject.net.flow.criteria.IPCriterion;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupBucket;
 import org.onosproject.net.group.GroupService;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.link.LinkService;
+import org.onosproject.t3.api.GroupsInDevice;
 import org.onosproject.t3.api.StaticPacketTrace;
 import org.onosproject.t3.api.TroubleshootService;
 import org.slf4j.Logger;
 
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.onlab.packet.EthType.EtherType;
+import static org.onosproject.net.flow.instructions.Instructions.GroupInstruction;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
- * Designates ...
+ * Manager to troubleshoot packets inside the network.
+ * Given a representation of a packet follows it's path in the network according to the existing flows and groups in
+ * the devices.
  */
 @Service
 @Component(immediate = true)
@@ -45,10 +87,606 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected GroupService groupService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected LinkService linkService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected HostService hostService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DriverService driverService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DeviceService deviceService;
 
     @Override
     public StaticPacketTrace trace(TrafficSelector packet, ConnectPoint in) {
-        return null;
+        log.info("Tracing packet {} coming in through {}", packet, in);
+        //device must exist in ONOS
+        Preconditions.checkNotNull(deviceService.getDevice(in.deviceId()),
+                "Device " + in.deviceId() + " must exist in ONOS");
+
+        StaticPacketTrace trace = new StaticPacketTrace(packet, in);
+        //FIXME this can be done recursively
+        trace = traceInDevice(trace, packet, in);
+        //Building output connect Points
+        List<ConnectPoint> path = new ArrayList<>();
+        trace = getTrace(path, in, trace);
+        return trace;
+    }
+
+    /**
+     * Computes a trace for a give packet that start in the network at the given connect point.
+     *
+     * @param completePath the path traversed by the packet
+     * @param in           the input connect point
+     * @param trace        the trace to build
+     * @return the build trace for that packet.
+     */
+    private StaticPacketTrace getTrace(List<ConnectPoint> completePath, ConnectPoint in, StaticPacketTrace trace) {
+
+        //if the trace already contains the input connect point there is a loop
+        if (pathContainsDevice(completePath, in.deviceId())) {
+            trace.addResultMessage("Loop encountered in device " + in.deviceId());
+            return trace;
+        }
+
+        //let's add the input connect point
+        completePath.add(in);
+
+        //If the trace has no outputs for the given input we stop here
+        if (trace.getGroupOuputs(in.deviceId()) == null) {
+            computePath(completePath, trace, null);
+            trace.addResultMessage("No output out of device " + in.deviceId() + ". Packet is dropped");
+            return trace;
+        }
+
+        //If the trace has ouputs we analyze them all
+        for (GroupsInDevice outputPath : trace.getGroupOuputs(in.deviceId())) {
+            log.debug("Output path {}", outputPath.getOutput());
+            //Hosts for the the given output
+            Set<Host> hostsList = hostService.getConnectedHosts(outputPath.getOutput());
+            //Hosts queried from the original ip or mac
+            Set<Host> hosts = getHosts(trace);
+
+            //If the two host collections contain the same item it means we reached the proper output
+            if (!Collections.disjoint(hostsList, hosts)) {
+                log.debug("Stopping here because host is expected destination");
+                trace.addResultMessage("Reached required destination Host");
+                computePath(completePath, trace, outputPath.getOutput());
+                break;
+            } else {
+                ConnectPoint cp = outputPath.getOutput();
+                //let's add the ouput for the input
+                completePath.add(cp);
+                log.debug("------------------------------------------------------------");
+                log.debug("Connect Point out {}", cp);
+                //let's compute the links for the given output
+                Set<Link> links = linkService.getEgressLinks(cp);
+                log.debug("Egress Links {}", links);
+                //No links means that the packet gets dropped.
+                if (links.size() == 0) {
+                    log.warn("No links out of {}", cp);
+                    computePath(completePath, trace, cp);
+                    trace.addResultMessage("No links depart from " + cp + ". Packet is dropped");
+                    return trace;
+                }
+                //For each link we trace the corresponding device
+                for (Link link : links) {
+                    ConnectPoint dst = link.dst();
+                    //change in-port to the dst link in port
+                    TrafficSelector.Builder updatedPacket = DefaultTrafficSelector.builder();
+                    outputPath.getFinalPacket().criteria().forEach(updatedPacket::add);
+                    updatedPacket.add(Criteria.matchInPort(dst.port()));
+                    log.debug("DST Connect Point {}", dst);
+                    //build the elements for that device
+                    traceInDevice(trace, updatedPacket.build(), dst);
+                    //continue the trace along the path
+                    getTrace(completePath, dst, trace);
+                }
+
+            }
+        }
+        return trace;
+    }
+
+    /**
+     * Checks if the path contains the device.
+     *
+     * @param completePath the path
+     * @param deviceId     the device to check
+     * @return true if the path contains the device
+     */
+    //TODO might prove costly, improvement: a class with both CPs and DeviceIds point.
+    private boolean pathContainsDevice(List<ConnectPoint> completePath, DeviceId deviceId) {
+        for (ConnectPoint cp : completePath) {
+            if (cp.deviceId().equals(deviceId)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Gets the hosts for the given initial packet.
+     *
+     * @param trace the trace we are building
+     * @return set of the hosts we are trying to reach
+     */
+    private Set<Host> getHosts(StaticPacketTrace trace) {
+        IPCriterion ipv4Criterion = ((IPCriterion) trace.getInitialPacket()
+                .getCriterion(Criterion.Type.IPV4_DST));
+        IPCriterion ipv6Criterion = ((IPCriterion) trace.getInitialPacket()
+                .getCriterion(Criterion.Type.IPV6_DST));
+        Set<Host> hosts = new HashSet<>();
+        if (ipv4Criterion != null) {
+            hosts.addAll(hostService.getHostsByIp(ipv4Criterion.ip().address()));
+        }
+        if (ipv6Criterion != null) {
+            hosts.addAll(hostService.getHostsByIp(ipv6Criterion.ip().address()));
+        }
+        EthCriterion ethCriterion = ((EthCriterion) trace.getInitialPacket()
+                .getCriterion(Criterion.Type.ETH_DST));
+        if (ethCriterion != null) {
+            hosts.addAll(hostService.getHostsByMac(ethCriterion.mac()));
+        }
+        return hosts;
+    }
+
+    /**
+     * Computes the list of traversed connect points.
+     *
+     * @param completePath the list of devices
+     * @param trace        the trace we are building
+     * @param output       the final output connect point
+     */
+    private void computePath(List<ConnectPoint> completePath, StaticPacketTrace trace, ConnectPoint output) {
+        List<ConnectPoint> traverseList = new ArrayList<>();
+        if (!completePath.contains(trace.getInitialConnectPoint())) {
+            traverseList.add(trace.getInitialConnectPoint());
+        }
+        traverseList.addAll(completePath);
+        if (output != null && !completePath.contains(output)) {
+            traverseList.add(output);
+        }
+        trace.addCompletePath(traverseList);
+        completePath.clear();
+    }
+
+    /**
+     * Traces the packet inside a device starting from an input connect point.
+     *
+     * @param trace  the trace we are building
+     * @param packet the packet we are tracing
+     * @param in     the input connect point.
+     * @return updated trace
+     */
+    private StaticPacketTrace traceInDevice(StaticPacketTrace trace, TrafficSelector packet, ConnectPoint in) {
+        log.debug("Packet {} coming in from {}", packet, in);
+
+        //if device is not available exit here.
+        if (!deviceService.isAvailable(in.deviceId())) {
+            trace.addResultMessage("Device is offline " + in.deviceId());
+            return trace;
+        }
+
+        List<FlowEntry> flows = new ArrayList<>();
+        List<FlowEntry> outputFlows = new ArrayList<>();
+
+        FlowEntry nextTableIdEntry = findNextTableIdEntry(in.deviceId(), -1);
+        if (nextTableIdEntry == null) {
+            trace.addResultMessage("No flow rules for device " + in.deviceId() + ". Aborting");
+            return trace;
+        }
+        TableId tableId = nextTableIdEntry.table();
+        FlowEntry flowEntry;
+        boolean output = false;
+        while (!output) {
+            log.debug("Searching a Flow Entry on table {} for packet {}", tableId, packet);
+            //get the rule that matches the incoming packet
+            flowEntry = matchHighestPriority(packet, in, tableId);
+            log.debug("Found Flow Entry {}", flowEntry);
+
+            boolean isOfdpaHardware = TroubleshootUtils.hardwareOfdpaMap
+                    .getOrDefault(driverService.getDriver(in.deviceId()).name(), false);
+
+            //if the flow entry on a table is null and we are on hardware we treat as table miss, with few exceptions
+            if (flowEntry == null && isOfdpaHardware) {
+                log.debug("Ofdpa Hw setup, no flow rule means table miss");
+
+                //Handling Hardware Specifics
+                if (((IndexTableId) tableId).id() == 27) {
+                    //Apparently a miss but Table 27 on OFDPA is a fixed table
+                    packet = handleOfdpa27FixedTable(trace, packet);
+                }
+
+                //Finding next table to go In case of miss
+                nextTableIdEntry = findNextTableIdEntry(in.deviceId(), ((IndexTableId) tableId).id());
+                log.debug("Next table id entry {}", nextTableIdEntry);
+
+                //FIXME find better solution that enable granularity greater than 0 or all rules
+                //(another possibility is max tableId)
+                if (nextTableIdEntry == null && flows.size() == 0) {
+                    trace.addResultMessage("No flow rules for device" + in.deviceId() + ". Aborting");
+                    return trace;
+
+                } else if (nextTableIdEntry == null) {
+                    //Means that no more flow rules are present
+                    output = true;
+
+                } else if (((IndexTableId) tableId).id() == 20) {
+                    //if the table is 20 OFDPA skips to table 50
+                    log.debug("A miss on Table 20 on OFDPA means that we skip directly to table 50");
+                    tableId = IndexTableId.of(50);
+
+                } else {
+                    tableId = nextTableIdEntry.table();
+                }
+
+
+            } else if (flowEntry == null) {
+                trace.addResultMessage("Packet has no match on table " + tableId + " in device " +
+                        in.deviceId() + ". Dropping");
+                return trace;
+            } else {
+                //IF the table has a transition
+                if (flowEntry.treatment().tableTransition() != null) {
+                    //update the next table we transitions to
+                    tableId = IndexTableId.of(flowEntry.treatment().tableTransition().tableId());
+                    log.debug("Flow Entry has transition to table Id {}", tableId);
+                    flows.add(flowEntry);
+                } else {
+                    //table has no transition so it means that it's an output rule if on the last table
+                    log.debug("Flow Entry has no transition to table, treating as last rule {}", flowEntry);
+                    flows.add(flowEntry);
+                    outputFlows.add(flowEntry);
+                    output = true;
+                }
+                //update the packet according to the actions of this flow rule.
+                packet = updatePacket(packet, flowEntry.treatment().allInstructions()).build();
+            }
+        }
+
+        //Creating a modifiable builder for the output packet
+        TrafficSelector.Builder builder = DefaultTrafficSelector.builder();
+        packet.criteria().forEach(builder::add);
+        //Adding all the flows to the trace
+        trace.addFlowsForDevice(in.deviceId(), flows);
+
+        log.debug("Flows traversed by {}", packet);
+        flows.forEach(entry -> {
+            log.debug("Flow {}", entry);
+        });
+
+        log.debug("Output Flows for {}", packet);
+        outputFlows.forEach(entry -> {
+            log.debug("Output Flow {}", entry);
+        });
+        List<PortNumber> outputPorts = new ArrayList<>();
+
+        //Decide Output for packet when flow rule contains an OUTPUT instruction
+        Set<Instruction> outputFlowEntries = outputFlows.stream().flatMap(flow -> {
+            return flow.treatment().allInstructions().stream();
+        })
+                .filter(instruction -> {
+                    return instruction.type().equals(Instruction.Type.OUTPUT);
+                }).collect(Collectors.toSet());
+        log.debug("Output instructions {}", outputFlowEntries);
+
+        if (outputFlowEntries.size() != 0) {
+            outputThroughFlows(trace, packet, in, builder, outputPorts, outputFlowEntries);
+
+        } else {
+            log.debug("Handling Groups");
+            //Analyze Groups
+            List<Group> groups = new ArrayList<>();
+
+            for (FlowEntry entry : flows) {
+                getGroupsFromInstructions(trace, groups, entry.treatment().allInstructions(),
+                        entry.deviceId(), builder, outputPorts, in);
+            }
+            packet = builder.build();
+            log.debug("Groups hit by packet {}", packet);
+            groups.forEach(group -> {
+                log.debug("Group {}", group);
+            });
+        }
+        log.debug("Output ports for packet {}", packet);
+        outputPorts.forEach(port -> {
+            log.debug("Port {}", port);
+        });
+        log.debug("Output Packet {}", packet);
+        return trace;
+    }
+
+    /**
+     * Method that saves the output if that si done via an OUTPUT treatment of a flow rule.
+     *
+     * @param trace             the trace
+     * @param packet            the packet coming in to this device
+     * @param in                the input connect point for this device
+     * @param builder           the updated packet0
+     * @param outputPorts       the list of output ports for this device
+     * @param outputFlowEntries the list of flow entries with OUTPUT treatment
+     */
+    private void outputThroughFlows(StaticPacketTrace trace, TrafficSelector packet, ConnectPoint in,
+                                    TrafficSelector.Builder builder, List<PortNumber> outputPorts,
+                                    Set<Instruction> outputFlowEntries) {
+        if (outputFlowEntries.size() > 1) {
+            log.warn("There cannot be more than one flow entry with OUTPUT instruction for {}", packet);
+        } else {
+            OutputInstruction outputInstruction = (OutputInstruction) outputFlowEntries.iterator().next();
+            //FIXME using GroupsInDevice for output even if flows.
+            buildOutputFromDevice(trace, in, builder, outputPorts, outputInstruction, ImmutableList.of());
+        }
+    }
+
+    /**
+     * Handles table 27 in Ofpda which is a fixed table not visible to any controller that handles Mpls Labels.
+     *
+     * @param packet the incoming packet
+     * @return the updated packet
+     */
+    private TrafficSelector handleOfdpa27FixedTable(StaticPacketTrace trace, TrafficSelector packet) {
+        log.debug("Handling table 27 on OFDPA, removing mpls ETH Type and change mpls label");
+        Criterion mplsCriterion = packet.getCriterion(Criterion.Type.ETH_TYPE);
+        ImmutableList.Builder<Instruction> builder = ImmutableList.builder();
+
+        //If the pakcet comes in with the expected elements we update it as per OFDPA spec.
+        if (mplsCriterion != null && ((EthTypeCriterion) mplsCriterion).ethType()
+                .equals(EtherType.MPLS_UNICAST.ethType())) {
+            //TODO update with parsing with eth MPLS pop Instruction for treating label an bos
+            Instruction ethInstruction = Instructions.popMpls(((EthTypeCriterion) trace.getInitialPacket()
+                    .getCriterion(Criterion.Type.ETH_TYPE)).ethType());
+            //FIXME what do we use as L3_Unicast mpls Label ?
+            //translateInstruction(builder, ethInstruction);
+            builder.add(ethInstruction);
+        }
+        packet = updatePacket(packet, builder.build()).build();
+        return packet;
+    }
+
+    /**
+     * Finds the flow entry with the minimun next table Id.
+     *
+     * @param deviceId  the device to search
+     * @param currentId the current id. the search will use this as minimum
+     * @return the flow entry with the minimum table Id after the given one.
+     */
+    private FlowEntry findNextTableIdEntry(DeviceId deviceId, int currentId) {
+
+        final Comparator<FlowEntry> comparator = Comparator.comparing((FlowEntry f) -> ((IndexTableId) f.table()).id());
+
+        return Lists.newArrayList(flowRuleService.getFlowEntries(deviceId).iterator())
+                .stream().filter(f -> ((IndexTableId) f.table()).id() > currentId).min(comparator).orElse(null);
+    }
+
+    /**
+     * Gets group information from instructions.
+     *
+     * @param trace           the trace we are building
+     * @param groupsForDevice the set of groups for this device
+     * @param instructions    the set of instructions we are searching for groups.
+     * @param deviceId        the device we are considering
+     * @param builder         the builder of the input packet
+     * @param outputPorts     the output ports for that packet
+     */
+    private void getGroupsFromInstructions(StaticPacketTrace trace, List<Group> groupsForDevice,
+                                           List<Instruction> instructions, DeviceId deviceId,
+                                           TrafficSelector.Builder builder, List<PortNumber> outputPorts,
+                                           ConnectPoint in) {
+        List<Instruction> groupInstructionlist = new ArrayList<>();
+        for (Instruction instruction : instructions) {
+            log.debug("Considering Instruction {}", instruction);
+            //if the instruction is not group we need to update the packet or add the output
+            //to the possible outputs for this packet
+            if (!instruction.type().equals(Instruction.Type.GROUP)) {
+                //if the instruction is not group we need to update the packet or add the output
+                //to the possible outputs for this packet
+                if (instruction.type().equals(Instruction.Type.OUTPUT)) {
+                    buildOutputFromDevice(trace, in, builder, outputPorts,
+                            (OutputInstruction) instruction, groupsForDevice);
+                } else {
+                    builder = translateInstruction(builder, instruction);
+                }
+            } else {
+                //if the instuction is pointing to a group we need to get the group
+                groupInstructionlist.add(instruction);
+            }
+        }
+        //handle all the internal instructions pointing to a group.
+        for (Instruction instr : groupInstructionlist) {
+            GroupInstruction groupInstruction = (GroupInstruction) instr;
+            Group group = Lists.newArrayList(groupService.getGroups(deviceId)).stream().filter(groupInternal -> {
+                return groupInternal.id().equals(groupInstruction.groupId());
+            }).findAny().orElse(null);
+            if (group == null) {
+                trace.addResultMessage("Null group for Instruction " + instr);
+                break;
+            }
+            //add the group to the traversed groups
+            groupsForDevice.add(group);
+            //Cycle in each of the group's buckets and add them to the groups for this Device.
+            for (GroupBucket bucket : group.buckets().buckets()) {
+                getGroupsFromInstructions(trace, groupsForDevice, bucket.treatment().allInstructions(),
+                        deviceId, builder, outputPorts, in);
+            }
+        }
+    }
+
+    /**
+     * Check if the output is the input port, if so adds a dop result message, otherwise builds
+     * a possible output from this device.
+     *
+     * @param trace             the trace
+     * @param in                the input connect point
+     * @param builder           the packet builder
+     * @param outputPorts       the list of output ports for this device
+     * @param outputInstruction the output instruction
+     * @param groupsForDevice
+     */
+    private void buildOutputFromDevice(StaticPacketTrace trace, ConnectPoint in, TrafficSelector.Builder builder,
+                                       List<PortNumber> outputPorts, OutputInstruction outputInstruction,
+                                       List<Group> groupsForDevice) {
+        ConnectPoint output = ConnectPoint.deviceConnectPoint(in.deviceId() + "/" +
+                outputInstruction.port());
+        if (output.equals(in)) {
+            trace.addResultMessage("Connect point out " + output + " is same as initial input " +
+                    trace.getInitialConnectPoint());
+        } else {
+            trace.addGroupOutputPath(in.deviceId(),
+                    new GroupsInDevice(output, groupsForDevice, builder.build()));
+            outputPorts.add(outputInstruction.port());
+        }
+    }
+
+    /**
+     * Applies all give instructions to the input packet.
+     *
+     * @param packet       the input packet
+     * @param instructions the set of instructions
+     * @return the packet with the applied instructions
+     */
+    private TrafficSelector.Builder updatePacket(TrafficSelector packet, List<Instruction> instructions) {
+        TrafficSelector.Builder newSelector = DefaultTrafficSelector.builder();
+        packet.criteria().forEach(newSelector::add);
+        //FIXME optimize
+        for (Instruction instruction : instructions) {
+            newSelector = translateInstruction(newSelector, instruction);
+        }
+        return newSelector;
+    }
+
+    /**
+     * Applies an instruction to the packet in the form of a selector.
+     *
+     * @param newSelector the packet selector
+     * @param instruction the instruction to be translated
+     * @return the new selector with the applied instruction
+     */
+    private TrafficSelector.Builder translateInstruction(TrafficSelector.Builder newSelector, Instruction instruction) {
+        log.debug("Translating instruction {}", instruction);
+        log.debug("New Selector {}", newSelector.build());
+        //TODO add as required
+        Criterion criterion = null;
+        switch (instruction.type()) {
+            case L2MODIFICATION:
+                L2ModificationInstruction l2Instruction = (L2ModificationInstruction) instruction;
+                switch (l2Instruction.subtype()) {
+                    case VLAN_ID:
+                        L2ModificationInstruction.ModVlanIdInstruction vlanIdInstruction =
+                                (L2ModificationInstruction.ModVlanIdInstruction) instruction;
+                        VlanId id = vlanIdInstruction.vlanId();
+                        criterion = Criteria.matchVlanId(id);
+                        break;
+                    case VLAN_POP:
+                        criterion = Criteria.matchVlanId(VlanId.NONE);
+                        break;
+                    case MPLS_PUSH:
+                        L2ModificationInstruction.ModMplsHeaderInstruction mplsEthInstruction =
+                                (L2ModificationInstruction.ModMplsHeaderInstruction) instruction;
+                        criterion = Criteria.matchEthType(mplsEthInstruction.ethernetType().toShort());
+                        break;
+                    case MPLS_POP:
+                        L2ModificationInstruction.ModMplsHeaderInstruction mplsPopInstruction =
+                                (L2ModificationInstruction.ModMplsHeaderInstruction) instruction;
+                        criterion = Criteria.matchEthType(mplsPopInstruction.ethernetType().toShort());
+
+                        //When popping MPLS we remove label and BOS
+                        TrafficSelector temporaryPacket = newSelector.build();
+                        if (temporaryPacket.getCriterion(Criterion.Type.MPLS_LABEL) != null) {
+                            TrafficSelector.Builder noMplsSelector = DefaultTrafficSelector.builder();
+                            temporaryPacket.criteria().stream().filter(c -> {
+                                return !c.type().equals(Criterion.Type.MPLS_LABEL) &&
+                                        !c.type().equals(Criterion.Type.MPLS_BOS);
+                            }).forEach(noMplsSelector::add);
+                            newSelector = noMplsSelector;
+                        }
+
+                        break;
+                    case MPLS_LABEL:
+                        L2ModificationInstruction.ModMplsLabelInstruction mplsLabelInstruction =
+                                (L2ModificationInstruction.ModMplsLabelInstruction) instruction;
+                        criterion = Criteria.matchMplsLabel(mplsLabelInstruction.label());
+                        newSelector.matchMplsBos(true);
+                        break;
+                    case ETH_DST:
+                        L2ModificationInstruction.ModEtherInstruction modEtherDstInstruction =
+                                (L2ModificationInstruction.ModEtherInstruction) instruction;
+                        criterion = Criteria.matchEthDst(modEtherDstInstruction.mac());
+                        break;
+                    case ETH_SRC:
+                        L2ModificationInstruction.ModEtherInstruction modEtherSrcInstruction =
+                                (L2ModificationInstruction.ModEtherInstruction) instruction;
+                        criterion = Criteria.matchEthSrc(modEtherSrcInstruction.mac());
+                        break;
+                    default:
+                        log.debug("Unsupported L2 Instruction");
+                        break;
+                }
+                break;
+            default:
+                log.debug("Unsupported Instruction");
+                break;
+        }
+        if (criterion != null) {
+            log.debug("Adding criterion {}", criterion);
+            newSelector.add(criterion);
+        }
+        return newSelector;
+    }
+
+    /**
+     * Finds the rule in the device that mathces the input packet and has the highest priority.
+     *
+     * @param packet  the input packet
+     * @param in      the connect point the packet comes in from
+     * @param tableId the table to search
+     * @return the flow entry
+     */
+    private FlowEntry matchHighestPriority(TrafficSelector packet, ConnectPoint in, TableId tableId) {
+        //Computing the possible match rules.
+        final Comparator<FlowEntry> comparator = Comparator.comparing(FlowRule::priority);
+        return Lists.newArrayList(flowRuleService.getFlowEntries(in.deviceId()).iterator())
+                .stream()
+                .filter(flowEntry -> {
+                    return flowEntry.table().equals(tableId);
+                })
+                .filter(flowEntry -> {
+                    return match(packet, flowEntry);
+                }).max(comparator).orElse(null);
+    }
+
+    /**
+     * Matches the packet with the given flow entry.
+     *
+     * @param packet    the packet to match
+     * @param flowEntry the flow entry to match the packet against
+     * @return true if the packet matches the flow.
+     */
+    private boolean match(TrafficSelector packet, FlowEntry flowEntry) {
+        //TODO handle MAC matching
+        return flowEntry.selector().criteria().stream().allMatch(criterion -> {
+            Criterion.Type type = criterion.type();
+            //If the critrion has IP we need to do LPM to establish matching.
+            if (type.equals(Criterion.Type.IPV4_SRC) || type.equals(Criterion.Type.IPV4_DST) ||
+                    type.equals(Criterion.Type.IPV6_SRC) || type.equals(Criterion.Type.IPV6_DST)) {
+                IPCriterion ipCriterion = (IPCriterion) criterion;
+                IPCriterion matchCriterion = (IPCriterion) packet.getCriterion(ipCriterion.type());
+                //if the packet does not have an IPv4 or IPv6 criterion we return true
+                if (matchCriterion == null) {
+                    return true;
+                }
+                try {
+                    Subnet subnet = Subnet.createInstance(ipCriterion.ip().toString());
+                    return subnet.isInSubnet(matchCriterion.ip().address().toInetAddress());
+                } catch (UnknownHostException e) {
+                    return false;
+                }
+                //we check that the packet contains the criterion provided by the flow rule.
+            } else {
+                return packet.criteria().contains(criterion);
+            }
+        });
     }
 }
diff --git a/apps/t3/src/main/java/org/onosproject/t3/impl/TroubleshootUtils.java b/apps/t3/src/main/java/org/onosproject/t3/impl/TroubleshootUtils.java
new file mode 100644
index 0000000..f89a413
--- /dev/null
+++ b/apps/t3/src/main/java/org/onosproject/t3/impl/TroubleshootUtils.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2018-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.t3.impl;
+
+import com.google.common.collect.ImmutableMap;
+
+import java.util.Map;
+
+/**
+ * Utility class for the troubleshooting tool.
+ */
+final class TroubleshootUtils {
+
+    private TroubleshootUtils() {
+        //Banning construction
+    }
+
+    /**
+     * Map defining if a specific driver is for a HW switch.
+     */
+    //Done with builder() instead of of() for clarity
+    static Map<String, Boolean> hardwareOfdpaMap = ImmutableMap.<String, Boolean>builder()
+            .put("ofdpa", true)
+            .put("ofdpa3", true)
+            .put("qmx-ofdpa3", true)
+            .put("as7712-32x-premium", true)
+            .put("as5912-54x-premium", true)
+            .put("as5916-54x-premium", true)
+            .put("accton-ofdpa3", true)
+            .put("znyx-ofdpa", true)
+            .build();
+}
diff --git a/apps/t3/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/t3/src/main/resources/OSGI-INF/blueprint/shell-config.xml
new file mode 100644
index 0000000..7da87bc
--- /dev/null
+++ b/apps/t3/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -0,0 +1,26 @@
+<!--
+  ~ 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.
+  -->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+
+    <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+        <command>
+            <action class="org.onosproject.t3.cli.TroubleshootTraceCommand"/>
+        </command>
+    </command-bundle>
+
+</blueprint>
+
+
diff --git a/apps/t3/src/test/java/org/onosproject/t3/impl/T3TestObjects.java b/apps/t3/src/test/java/org/onosproject/t3/impl/T3TestObjects.java
new file mode 100644
index 0000000..15cee5f
--- /dev/null
+++ b/apps/t3/src/test/java/org/onosproject/t3/impl/T3TestObjects.java
@@ -0,0 +1,356 @@
+/*
+ * Copyright 2018-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.t3.impl;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import org.onlab.packet.EthType;
+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.core.DefaultApplicationId;
+import org.onosproject.core.GroupId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultHost;
+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.flow.DefaultFlowEntry;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.group.DefaultGroup;
+import org.onosproject.net.group.DefaultGroupBucket;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.provider.ProviderId;
+
+/**
+ * Helper class for objects related to the Troubleshoot Manager Test.
+ */
+final class T3TestObjects {
+
+    private T3TestObjects(){
+        //banning construction
+    }
+
+    private static final String HOST_ONE_MAC = "00:00:00:00:00:01";
+    private static final String HOST_TWO_MAC = "00:00:00:00:00:02";
+    private static final String HOST_ONE_VLAN = "None";
+    private static final String HOST_TWO_VLAN = "None";
+    private static final String HOST_ONE = HOST_ONE_MAC + "/" + HOST_ONE_VLAN;
+    private static final String HOST_TWO = HOST_TWO_MAC + "/" + HOST_TWO_VLAN;
+
+    //offline device
+    static final DeviceId OFFLINE_DEVICE = DeviceId.deviceId("offlineDevice");
+
+    //Single Flow Test
+    static final DeviceId SINGLE_FLOW_DEVICE = DeviceId.deviceId("SingleFlowDevice");
+    private static final TrafficSelector SINGLE_FLOW_SELECTOR = DefaultTrafficSelector.builder()
+            .matchInPort(PortNumber.portNumber(1))
+            .matchIPSrc(IpPrefix.valueOf("127.0.0.1/32"))
+            .matchIPDst(IpPrefix.valueOf("127.0.0.2/32"))
+            .build();
+
+    private static final TrafficTreatment OUTPUT_FLOW_TREATMENT = DefaultTrafficTreatment.builder()
+            .setOutput(PortNumber.portNumber(2)).build();
+    private static final FlowRule SINGLE_FLOW = DefaultFlowEntry.builder().forDevice(SINGLE_FLOW_DEVICE)
+            .forTable(0)
+            .withPriority(100)
+            .withSelector(SINGLE_FLOW_SELECTOR)
+            .withTreatment(OUTPUT_FLOW_TREATMENT)
+            .fromApp(new DefaultApplicationId(0, "TestApp"))
+            .makePermanent()
+            .build();
+    static final FlowEntry SINGLE_FLOW_ENTRY = new DefaultFlowEntry(SINGLE_FLOW);
+
+    static final ConnectPoint SINGLE_FLOW_IN_CP = ConnectPoint.deviceConnectPoint(SINGLE_FLOW_DEVICE + "/" + 1);
+
+    static final ConnectPoint SINGLE_FLOW_OUT_CP = ConnectPoint.deviceConnectPoint(SINGLE_FLOW_DEVICE + "/" + 2);
+
+    //same output as input
+    static final DeviceId SAME_OUTPUT_FLOW_DEVICE = DeviceId.deviceId("sameOutputDevice");
+
+    private static final TrafficTreatment SAME_OUTPUT_FLOW_TREATMENT = DefaultTrafficTreatment.builder()
+            .setOutput(PortNumber.portNumber(1)).build();
+    private static final FlowRule SAME_OUTPUT_FLOW = DefaultFlowEntry.builder().forDevice(SAME_OUTPUT_FLOW_DEVICE)
+            .forTable(0)
+            .withPriority(100)
+            .withSelector(SINGLE_FLOW_SELECTOR)
+            .withTreatment(SAME_OUTPUT_FLOW_TREATMENT)
+            .fromApp(new DefaultApplicationId(0, "TestApp"))
+            .makePermanent()
+            .build();
+    static final FlowEntry SAME_OUTPUT_FLOW_ENTRY = new DefaultFlowEntry(SAME_OUTPUT_FLOW);
+
+    static final ConnectPoint SAME_OUTPUT_FLOW_CP = ConnectPoint.deviceConnectPoint(SAME_OUTPUT_FLOW_DEVICE + "/" + 1);
+
+
+    //Dual Flow Test
+    static final DeviceId DUAL_FLOW_DEVICE = DeviceId.deviceId("DualFlowDevice");
+    private static final TrafficTreatment TRANSITION_FLOW_TREATMENT = DefaultTrafficTreatment.builder()
+            .setVlanId(VlanId.vlanId((short) 100))
+            .transition(10)
+            .build();
+    private static final TrafficSelector VLAN_FLOW_SELECTOR = DefaultTrafficSelector.builder()
+            .matchVlanId(VlanId.vlanId((short) 100))
+            .build();
+    private static final FlowRule FIRST_FLOW = DefaultFlowEntry.builder().forDevice(DUAL_FLOW_DEVICE)
+            .forTable(0)
+            .withPriority(100)
+            .withSelector(SINGLE_FLOW_SELECTOR)
+            .withTreatment(TRANSITION_FLOW_TREATMENT)
+            .fromApp(new DefaultApplicationId(0, "TestApp"))
+            .makePermanent()
+            .build();
+    static final FlowEntry FIRST_FLOW_ENTRY = new DefaultFlowEntry(FIRST_FLOW);
+    private static final FlowRule SECOND_FLOW = DefaultFlowEntry.builder().forDevice(DUAL_FLOW_DEVICE)
+            .forTable(10)
+            .withPriority(100)
+            .withSelector(VLAN_FLOW_SELECTOR)
+            .withTreatment(OUTPUT_FLOW_TREATMENT)
+            .fromApp(new DefaultApplicationId(0, "TestApp"))
+            .makePermanent()
+            .build();
+    static final FlowEntry SECOND_FLOW_ENTRY = new DefaultFlowEntry(SECOND_FLOW);
+
+    static final ConnectPoint DUAL_FLOW_IN_CP = ConnectPoint.deviceConnectPoint(DUAL_FLOW_DEVICE + "/" + 1);
+
+    static final ConnectPoint DUAL_FLOW_OUT_CP = ConnectPoint.deviceConnectPoint(DUAL_FLOW_DEVICE + "/" + 2);
+
+    //Flow and Group Test
+    static final DeviceId GROUP_FLOW_DEVICE = DeviceId.deviceId("GroupFlowDevice");
+
+    private static final GroupId GROUP_ID = GroupId.valueOf(1);
+
+    private static final TrafficTreatment GROUP_FLOW_TREATMENT = DefaultTrafficTreatment.builder()
+            .pushMpls()
+            .setMpls(MplsLabel.mplsLabel(100))
+            .group(GROUP_ID)
+            .build();
+    private static final FlowRule GROUP_FLOW = DefaultFlowEntry.builder().forDevice(GROUP_FLOW_DEVICE)
+            .forTable(0)
+            .withPriority(100)
+            .withSelector(SINGLE_FLOW_SELECTOR)
+            .withTreatment(GROUP_FLOW_TREATMENT)
+            .fromApp(new DefaultApplicationId(0, "TestApp"))
+            .makePermanent()
+            .build();
+    static final FlowEntry GROUP_FLOW_ENTRY = new DefaultFlowEntry(GROUP_FLOW);
+
+    private static final TrafficTreatment OUTPUT_GROUP_TREATMENT = DefaultTrafficTreatment.builder()
+            .popMpls(EthType.EtherType.IPV4.ethType())
+            .setOutput(PortNumber.portNumber(2)).build();
+
+    private static final GroupBucket BUCKET = DefaultGroupBucket.createSelectGroupBucket(OUTPUT_GROUP_TREATMENT);
+
+    private static final GroupBuckets BUCKETS = new GroupBuckets(ImmutableList.of(BUCKET));
+
+    static final Group GROUP = new DefaultGroup(GROUP_ID, GROUP_FLOW_DEVICE, Group.Type.SELECT, BUCKETS);
+
+    static final ConnectPoint GROUP_FLOW_IN_CP = ConnectPoint.deviceConnectPoint(GROUP_FLOW_DEVICE + "/" + 1);
+
+    static final ConnectPoint GROUP_FLOW_OUT_CP = ConnectPoint.deviceConnectPoint(GROUP_FLOW_DEVICE + "/" + 2);
+
+    //topology
+
+    static final DeviceId TOPO_FLOW_DEVICE = DeviceId.deviceId("SingleFlowDevice1");
+
+    static final DeviceId TOPO_FLOW_2_DEVICE = DeviceId.deviceId("SingleFlowDevice2");
+
+    static final DeviceId TOPO_FLOW_3_DEVICE = DeviceId.deviceId("SingleFlowDevice3");
+
+    private static final TrafficSelector TOPO_FLOW_SELECTOR = DefaultTrafficSelector.builder()
+            .matchInPort(PortNumber.portNumber(1))
+            .matchIPSrc(IpPrefix.valueOf("127.0.0.1/32"))
+            .matchIPDst(IpPrefix.valueOf("127.0.0.3/32"))
+            .build();
+
+    private static final FlowRule TOPO_SINGLE_FLOW = DefaultFlowEntry.builder().forDevice(TOPO_FLOW_DEVICE)
+            .forTable(0)
+            .withPriority(100)
+            .withSelector(TOPO_FLOW_SELECTOR)
+            .withTreatment(OUTPUT_FLOW_TREATMENT)
+            .fromApp(new DefaultApplicationId(0, "TestApp"))
+            .makePermanent()
+            .build();
+
+    static final FlowEntry TOPO_SINGLE_FLOW_ENTRY = new DefaultFlowEntry(TOPO_SINGLE_FLOW);
+
+    static final ConnectPoint TOPO_FLOW_1_IN_CP = ConnectPoint.deviceConnectPoint(TOPO_FLOW_DEVICE + "/" + 1);
+
+    static final ConnectPoint TOPO_FLOW_1_OUT_CP = ConnectPoint.deviceConnectPoint(TOPO_FLOW_DEVICE + "/" + 2);
+
+    static final ConnectPoint TOPO_FLOW_2_IN_CP = ConnectPoint.deviceConnectPoint(TOPO_FLOW_2_DEVICE + "/" + 1);
+
+    static final ConnectPoint TOPO_FLOW_2_OUT_CP = ConnectPoint.deviceConnectPoint(TOPO_FLOW_2_DEVICE + "/" + 2);
+
+    static final ConnectPoint TOPO_FLOW_3_IN_CP = ConnectPoint.deviceConnectPoint(TOPO_FLOW_3_DEVICE + "/" + 1);
+
+    static final ConnectPoint TOPO_FLOW_3_OUT_CP = ConnectPoint.deviceConnectPoint(TOPO_FLOW_3_DEVICE + "/" + 2);
+
+
+    //Topology with Groups
+
+    static final DeviceId TOPO_GROUP_FLOW_DEVICE = DeviceId.deviceId("TopoGroupFlowDevice");
+
+    private static final TrafficSelector TOPO_SECOND_INPUT_FLOW_SELECTOR = DefaultTrafficSelector.builder()
+            .matchInPort(PortNumber.portNumber(3))
+            .matchIPSrc(IpPrefix.valueOf("127.0.0.1/32"))
+            .matchIPDst(IpPrefix.valueOf("127.0.0.3/32"))
+            .build();
+
+    private static final FlowRule TOPO_SECOND_INPUT_FLOW = DefaultFlowEntry.builder().forDevice(TOPO_FLOW_3_DEVICE)
+            .forTable(0)
+            .withPriority(100)
+            .withSelector(TOPO_SECOND_INPUT_FLOW_SELECTOR)
+            .withTreatment(OUTPUT_FLOW_TREATMENT)
+            .fromApp(new DefaultApplicationId(0, "TestApp"))
+            .makePermanent()
+            .build();
+
+    private static final TrafficTreatment OUTPUT_2_FLOW_TREATMENT = DefaultTrafficTreatment.builder()
+            .setOutput(PortNumber.portNumber(3)).build();
+
+
+    private static final GroupId TOPO_GROUP_ID = GroupId.valueOf(1);
+
+    private static final TrafficTreatment TOPO_GROUP_FLOW_TREATMENT = DefaultTrafficTreatment.builder()
+            .group(TOPO_GROUP_ID)
+            .build();
+    private static final FlowRule TOPO_GROUP_FLOW = DefaultFlowEntry.builder().forDevice(TOPO_GROUP_FLOW_DEVICE)
+            .forTable(0)
+            .withPriority(100)
+            .withSelector(TOPO_FLOW_SELECTOR)
+            .withTreatment(TOPO_GROUP_FLOW_TREATMENT)
+            .fromApp(new DefaultApplicationId(0, "TestApp"))
+            .makePermanent()
+            .build();
+    static final FlowEntry TOPO_GROUP_FLOW_ENTRY = new DefaultFlowEntry(TOPO_GROUP_FLOW);
+
+    private static final GroupBucket BUCKET_2 = DefaultGroupBucket.createSelectGroupBucket(OUTPUT_2_FLOW_TREATMENT);
+
+    private static final GroupBuckets BUCKETS_MULTIPLE = new GroupBuckets(ImmutableList.of(BUCKET, BUCKET_2));
+
+    static final Group TOPO_GROUP = new DefaultGroup(TOPO_GROUP_ID, TOPO_GROUP_FLOW_DEVICE,
+            Group.Type.SELECT, BUCKETS_MULTIPLE);
+
+    static final FlowEntry TOPO_SECOND_INPUT_FLOW_ENTRY = new DefaultFlowEntry(TOPO_SECOND_INPUT_FLOW);
+
+    static final DeviceId TOPO_FLOW_4_DEVICE = DeviceId.deviceId("SingleFlowDevice4");
+
+    static final ConnectPoint TOPO_FLOW_IN_CP = ConnectPoint.deviceConnectPoint(TOPO_GROUP_FLOW_DEVICE + "/" + 1);
+
+    static final ConnectPoint TOPO_FLOW_OUT_CP_1 = ConnectPoint.deviceConnectPoint(TOPO_GROUP_FLOW_DEVICE + "/" + 2);
+
+    protected static final ConnectPoint TOPO_FLOW_OUT_CP_2 =
+            ConnectPoint.deviceConnectPoint(TOPO_GROUP_FLOW_DEVICE + "/" + 3);
+
+    static final ConnectPoint TOPO_FLOW_4_IN_CP = ConnectPoint.deviceConnectPoint(TOPO_FLOW_4_DEVICE + "/" + 1);
+
+    static final ConnectPoint TOPO_FLOW_3_IN_2_CP = ConnectPoint.deviceConnectPoint(TOPO_FLOW_3_DEVICE + "/" + 3);
+
+    static final ConnectPoint TOPO_FLOW_4_OUT_CP = ConnectPoint.deviceConnectPoint(TOPO_FLOW_4_DEVICE + "/" + 2);
+
+
+    //hardware
+
+    static final DeviceId HARDWARE_DEVICE = DeviceId.deviceId("HardwareDevice");
+
+    static final ConnectPoint HARDWARE_DEVICE_IN_CP = ConnectPoint.deviceConnectPoint(HARDWARE_DEVICE + "/" + 1);
+
+    static final ConnectPoint HARDWARE_DEVICE_OUT_CP = ConnectPoint.deviceConnectPoint(HARDWARE_DEVICE + "/" + 2);
+
+    private static final TrafficSelector HARDWARE_FLOW_SELECTOR = DefaultTrafficSelector.builder()
+            .matchInPort(PortNumber.portNumber(1))
+            .matchIPSrc(IpPrefix.valueOf("127.0.0.1/32"))
+            .matchIPDst(IpPrefix.valueOf("127.0.0.2/32"))
+            .build();
+
+    private static final TrafficTreatment HW_TRANSITION_FLOW_TREATMENT = DefaultTrafficTreatment.builder()
+            .pushMpls()
+            .transition(27)
+            .build();
+
+    private static final FlowRule HARDWARE_FLOW = DefaultFlowEntry.builder().forDevice(TOPO_FLOW_3_DEVICE)
+            .forTable(0)
+            .withPriority(100)
+            .withSelector(HARDWARE_FLOW_SELECTOR)
+            .withTreatment(HW_TRANSITION_FLOW_TREATMENT)
+            .fromApp(new DefaultApplicationId(0, "TestApp"))
+            .makePermanent()
+            .build();
+
+    static final FlowEntry HARDWARE_FLOW_ENTRY = new DefaultFlowEntry(HARDWARE_FLOW);
+
+    private static final TrafficSelector HARDWARE_ETH_FLOW_SELECTOR = DefaultTrafficSelector.builder()
+            .matchInPort(PortNumber.portNumber(1))
+            .matchIPSrc(IpPrefix.valueOf("127.0.0.1/32"))
+            .matchIPDst(IpPrefix.valueOf("127.0.0.2/32"))
+            .matchEthType(EthType.EtherType.IPV4.ethType().toShort())
+            .build();
+
+    private static final FlowRule HARDWARE_ETH_FLOW = DefaultFlowEntry.builder().forDevice(TOPO_FLOW_3_DEVICE)
+            .forTable(30)
+            .withPriority(100)
+            .withSelector(HARDWARE_ETH_FLOW_SELECTOR)
+            .withTreatment(OUTPUT_FLOW_TREATMENT)
+            .fromApp(new DefaultApplicationId(0, "TestApp"))
+            .makePermanent()
+            .build();
+
+    static final FlowEntry HARDWARE_ETH_FLOW_ENTRY = new DefaultFlowEntry(HARDWARE_ETH_FLOW);
+
+
+
+
+    //helper elements
+
+    static final Host H1 = new DefaultHost(ProviderId.NONE, HostId.hostId(HOST_ONE), MacAddress.valueOf(100),
+            VlanId.NONE, new HostLocation(SINGLE_FLOW_DEVICE, PortNumber.portNumber(2), 0),
+            ImmutableSet.of(IpAddress.valueOf("127.0.0.2")));
+
+    static final Host H2 = new DefaultHost(ProviderId.NONE, HostId.hostId(HOST_TWO), MacAddress.valueOf(100),
+            VlanId.NONE, new HostLocation(TOPO_FLOW_3_DEVICE, PortNumber.portNumber(2), 0),
+            ImmutableSet.of(IpAddress.valueOf("127.0.0.3")));
+
+    static final TrafficSelector PACKET_OK = DefaultTrafficSelector.builder()
+            .matchInPort(PortNumber.portNumber(1))
+            .matchEthType(EthType.EtherType.IPV4.ethType().toShort())
+            .matchIPSrc(IpPrefix.valueOf("127.0.0.1/32"))
+            .matchIPDst(IpPrefix.valueOf("127.0.0.2/32"))
+            .build();
+
+    static final TrafficSelector PACKET_OK_TOPO = DefaultTrafficSelector.builder()
+            .matchInPort(PortNumber.portNumber(1))
+            .matchIPSrc(IpPrefix.valueOf("127.0.0.1/32"))
+            .matchIPDst(IpPrefix.valueOf("127.0.0.3/32"))
+            .build();
+
+    static final TrafficSelector PACKET_FAIL = DefaultTrafficSelector.builder()
+            .matchInPort(PortNumber.portNumber(1))
+            .matchIPSrc(IpPrefix.valueOf("127.0.0.1/32"))
+            .matchIPDst(IpPrefix.valueOf("127.0.0.99/32"))
+            .build();
+}
diff --git a/apps/t3/src/test/java/org/onosproject/t3/impl/TroubleshootManagerTest.java b/apps/t3/src/test/java/org/onosproject/t3/impl/TroubleshootManagerTest.java
new file mode 100644
index 0000000..a8f6524
--- /dev/null
+++ b/apps/t3/src/test/java/org/onosproject/t3/impl/TroubleshootManagerTest.java
@@ -0,0 +1,403 @@
+/*
+ * Copyright 2018-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.t3.impl;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.ChassisId;
+import org.onlab.packet.EthType;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DefaultDevice;
+import org.onosproject.net.DefaultLink;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.Link;
+import org.onosproject.net.device.DeviceServiceAdapter;
+import org.onosproject.net.driver.DefaultDriver;
+import org.onosproject.net.driver.Driver;
+import org.onosproject.net.driver.DriverServiceAdapter;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRuleServiceAdapter;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.EthTypeCriterion;
+import org.onosproject.net.flow.criteria.VlanIdCriterion;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupServiceAdapter;
+import org.onosproject.net.host.HostServiceAdapter;
+import org.onosproject.net.link.LinkServiceAdapter;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.t3.api.StaticPacketTrace;
+import org.slf4j.Logger;
+
+import java.util.HashMap;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.onosproject.net.Device.Type.SWITCH;
+import static org.onosproject.t3.impl.T3TestObjects.*;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Test Class for Troubleshoot Manager.
+ */
+public class TroubleshootManagerTest {
+
+    private static final Logger log = getLogger(TroubleshootManager.class);
+
+    private TroubleshootManager mngr;
+
+    @Before
+    public void setUp() throws Exception {
+        mngr = new TroubleshootManager();
+        mngr.flowRuleService = new TestFlowRuleService();
+        mngr.hostService = new TestHostService();
+        mngr.linkService = new TestLinkService();
+        mngr.driverService = new TestDriverService();
+        mngr.groupService = new TestGroupService();
+        mngr.deviceService = new TestDeviceService();
+
+        assertNotNull("Manager should not be null", mngr);
+
+        assertNotNull("Flow rule Service should not be null", mngr.flowRuleService);
+        assertNotNull("Host Service should not be null", mngr.hostService);
+        assertNotNull("Group Service should not be null", mngr.groupService);
+        assertNotNull("Driver Service should not be null", mngr.driverService);
+        assertNotNull("Link Service should not be null", mngr.linkService);
+        assertNotNull("Device Service should not be null", mngr.deviceService);
+    }
+
+    /**
+     * Tests failure on non existent device.
+     */
+    @Test(expected = NullPointerException.class)
+    public void nonExistentDevice() {
+        StaticPacketTrace traceFail = mngr.trace(PACKET_OK, ConnectPoint.deviceConnectPoint("nonexistent" + "/1"));
+    }
+
+    /**
+     * Tests failure on offline device.
+     */
+    @Test
+    public void offlineDevice() {
+        StaticPacketTrace traceFail = mngr.trace(PACKET_OK, ConnectPoint.deviceConnectPoint(OFFLINE_DEVICE + "/1"));
+        assertNotNull("Trace should not be null", traceFail);
+        assertNull("Trace should have 0 output", traceFail.getGroupOuputs(SINGLE_FLOW_DEVICE));
+    }
+
+    /**
+     * Tests failure on same output.
+     */
+    @Test
+    public void sameOutput() {
+        StaticPacketTrace traceFail = mngr.trace(PACKET_OK, SAME_OUTPUT_FLOW_CP);
+        assertNotNull("Trace should not be null", traceFail);
+        assertTrue("Trace should be unsuccessful",
+                traceFail.resultMessage().contains("is same as initial input"));
+        log.info("trace {}", traceFail.resultMessage());
+    }
+
+
+    /**
+     * Tests failure on device with no flows.
+     */
+    @Test
+    public void noFlows() {
+        StaticPacketTrace traceFail = mngr.trace(PACKET_OK, ConnectPoint.deviceConnectPoint("test/1"));
+        assertNotNull("Trace should not be null", traceFail);
+        assertNull("Trace should have 0 output", traceFail.getGroupOuputs(SINGLE_FLOW_DEVICE));
+        log.info("trace {}", traceFail.resultMessage());
+    }
+
+    /**
+     * Test a single flow rule that has output port in it.
+     */
+    @Test
+    public void testSingleFlowRule() {
+
+        testSuccess(PACKET_OK, SINGLE_FLOW_IN_CP, SINGLE_FLOW_DEVICE, SINGLE_FLOW_OUT_CP, 1);
+
+        testFaliure(PACKET_FAIL, SINGLE_FLOW_IN_CP, SINGLE_FLOW_DEVICE);
+    }
+
+    /**
+     * Tests two flow rule the last one of which has output port in it.
+     */
+    @Test
+    public void testDualFlowRule() {
+
+        //Test Success
+
+        StaticPacketTrace traceSuccess = testSuccess(PACKET_OK, DUAL_FLOW_IN_CP, DUAL_FLOW_DEVICE,
+                DUAL_FLOW_OUT_CP, 1);
+
+        //Testing Vlan
+        Criterion criterion = traceSuccess.getGroupOuputs(DUAL_FLOW_DEVICE).get(0).
+                getFinalPacket().getCriterion(Criterion.Type.VLAN_VID);
+        assertNotNull("Packet Should have Vlan", criterion);
+
+        VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) criterion;
+
+        assertEquals("Vlan should be 100", VlanId.vlanId((short) 100), vlanIdCriterion.vlanId());
+
+        //Test Faliure
+        testFaliure(PACKET_FAIL, DUAL_FLOW_IN_CP, DUAL_FLOW_DEVICE);
+
+    }
+
+    /**
+     * Test a single flow rule that points to a group with output port in it.
+     */
+    @Test
+    public void flowAndGroup() throws Exception {
+
+        StaticPacketTrace traceSuccess = testSuccess(PACKET_OK, GROUP_FLOW_IN_CP, GROUP_FLOW_DEVICE,
+                GROUP_FLOW_OUT_CP, 1);
+
+        assertTrue("Wrong Output Group", traceSuccess.getGroupOuputs(GROUP_FLOW_DEVICE)
+                .get(0).getGroups().contains(GROUP));
+        assertEquals("Packet should not have MPLS Label", EthType.EtherType.IPV4.ethType(),
+                ((EthTypeCriterion) traceSuccess.getGroupOuputs(GROUP_FLOW_DEVICE)
+                        .get(0).getFinalPacket().getCriterion(Criterion.Type.ETH_TYPE)).ethType());
+        assertNull("Packet should not have MPLS Label", traceSuccess.getGroupOuputs(GROUP_FLOW_DEVICE)
+                .get(0).getFinalPacket().getCriterion(Criterion.Type.MPLS_LABEL));
+        assertNull("Packet should not have MPLS Label", traceSuccess.getGroupOuputs(GROUP_FLOW_DEVICE)
+                .get(0).getFinalPacket().getCriterion(Criterion.Type.MPLS_BOS));
+
+    }
+
+    /**
+     * Test path through a 3 device topology.
+     */
+    @Test
+    public void singlePathTopology() throws Exception {
+
+        StaticPacketTrace traceSuccess = testSuccess(PACKET_OK_TOPO, TOPO_FLOW_1_IN_CP,
+                TOPO_FLOW_3_DEVICE, TOPO_FLOW_3_OUT_CP, 1);
+
+        assertTrue("Incorrect path",
+                traceSuccess.getCompletePaths().get(0).contains(TOPO_FLOW_2_IN_CP));
+        assertTrue("Incorrect path",
+                traceSuccess.getCompletePaths().get(0).contains(TOPO_FLOW_2_OUT_CP));
+        assertTrue("Incorrect path",
+                traceSuccess.getCompletePaths().get(0).contains(TOPO_FLOW_3_IN_CP));
+
+    }
+
+    /**
+     * Test path through a 4 device topology with first device that has groups with multiple output buckets.
+     */
+    @Test
+    public void testGroupTopo() throws Exception {
+
+        StaticPacketTrace traceSuccess = testSuccess(PACKET_OK_TOPO, TOPO_FLOW_IN_CP,
+                TOPO_FLOW_3_DEVICE, TOPO_FLOW_3_OUT_CP, 2);
+
+        assertTrue("Incorrect groups",
+                traceSuccess.getGroupOuputs(TOPO_GROUP_FLOW_DEVICE).get(0).getGroups().contains(TOPO_GROUP));
+        assertTrue("Incorrect bucket",
+                traceSuccess.getGroupOuputs(TOPO_GROUP_FLOW_DEVICE).get(1).getGroups().contains(TOPO_GROUP));
+    }
+
+    /**
+     * Test HW support in a single device with 2 flow rules to check hit of static HW rules.
+     */
+    @Test
+    public void hardwareTest() throws Exception {
+
+        StaticPacketTrace traceSuccess = testSuccess(PACKET_OK, HARDWARE_DEVICE_IN_CP,
+                HARDWARE_DEVICE, HARDWARE_DEVICE_OUT_CP, 1);
+
+        assertEquals("wrong ETH type", EthType.EtherType.IPV4.ethType(),
+                ((EthTypeCriterion) traceSuccess.getGroupOuputs(HARDWARE_DEVICE).get(0).getFinalPacket()
+                        .getCriterion(Criterion.Type.ETH_TYPE)).ethType());
+
+    }
+
+    private StaticPacketTrace testSuccess(TrafficSelector packet, ConnectPoint in, DeviceId deviceId, ConnectPoint out,
+                                          int paths) {
+        StaticPacketTrace traceSuccess = mngr.trace(packet, in);
+
+        log.info("trace {}", traceSuccess);
+
+        log.info("trace {}", traceSuccess.resultMessage());
+
+        assertNotNull("trace should not be null", traceSuccess);
+        assertEquals("Trace should have " + paths + " output", paths, traceSuccess.getGroupOuputs(deviceId).size());
+        assertEquals("Trace should only have " + paths + "output", paths, traceSuccess.getCompletePaths().size());
+        assertTrue("Trace should be successful",
+                traceSuccess.resultMessage().contains("Reached required destination Host"));
+        assertEquals("Incorrect Output CP", out,
+                traceSuccess.getGroupOuputs(deviceId).get(0).getOutput());
+
+        return traceSuccess;
+    }
+
+    private void testFaliure(TrafficSelector packet, ConnectPoint in, DeviceId deviceId) {
+        StaticPacketTrace traceFail = mngr.trace(packet, in);
+
+        log.info("trace {}", traceFail.resultMessage());
+
+        assertNotNull("Trace should not be null", traceFail);
+        assertNull("Trace should have 0 output", traceFail.getGroupOuputs(deviceId));
+    }
+
+    private class TestFlowRuleService extends FlowRuleServiceAdapter {
+        @Override
+        public Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
+            if (deviceId.equals(SINGLE_FLOW_DEVICE)) {
+                return ImmutableList.of(SINGLE_FLOW_ENTRY);
+            } else if (deviceId.equals(DUAL_FLOW_DEVICE)) {
+                return ImmutableList.of(FIRST_FLOW_ENTRY, SECOND_FLOW_ENTRY);
+            } else if (deviceId.equals(GROUP_FLOW_DEVICE)) {
+                return ImmutableList.of(GROUP_FLOW_ENTRY);
+            } else if (deviceId.equals(TOPO_FLOW_DEVICE) ||
+                    deviceId.equals(TOPO_FLOW_2_DEVICE) ||
+                    deviceId.equals(TOPO_FLOW_3_DEVICE) ||
+                    deviceId.equals(TOPO_FLOW_4_DEVICE)) {
+                return ImmutableList.of(TOPO_SINGLE_FLOW_ENTRY, TOPO_SECOND_INPUT_FLOW_ENTRY);
+            } else if (deviceId.equals(TOPO_GROUP_FLOW_DEVICE)) {
+                return ImmutableList.of(TOPO_GROUP_FLOW_ENTRY);
+            } else if (deviceId.equals(HARDWARE_DEVICE)) {
+                return ImmutableList.of(HARDWARE_ETH_FLOW_ENTRY, HARDWARE_FLOW_ENTRY);
+            } else if (deviceId.equals(SAME_OUTPUT_FLOW_DEVICE)) {
+                return ImmutableList.of(SAME_OUTPUT_FLOW_ENTRY);
+            }
+            return ImmutableList.of();
+        }
+    }
+
+    private class TestDriverService extends DriverServiceAdapter {
+        @Override
+        public Driver getDriver(DeviceId deviceId) {
+            if (deviceId.equals(HARDWARE_DEVICE)) {
+                return new DefaultDriver("ofdpa", ImmutableList.of(),
+                        "test", "test", "test", new HashMap<>(), new HashMap<>());
+            }
+            return new DefaultDriver("NotHWDriver", ImmutableList.of(),
+                    "test", "test", "test", new HashMap<>(), new HashMap<>());
+        }
+    }
+
+    private class TestGroupService extends GroupServiceAdapter {
+        @Override
+        public Iterable<Group> getGroups(DeviceId deviceId) {
+            if (deviceId.equals(GROUP_FLOW_DEVICE)) {
+                return ImmutableList.of(GROUP);
+            } else if (deviceId.equals(TOPO_GROUP_FLOW_DEVICE)) {
+                return ImmutableList.of(TOPO_GROUP);
+            }
+            return ImmutableList.of();
+        }
+    }
+
+    private class TestHostService extends HostServiceAdapter {
+        @Override
+        public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
+            if (connectPoint.equals(TOPO_FLOW_3_OUT_CP)) {
+                return ImmutableSet.of(H2);
+            }
+            return ImmutableSet.of(H1);
+        }
+
+        @Override
+        public Set<Host> getHostsByMac(MacAddress mac) {
+            if (mac.equals(H1.mac())) {
+                return ImmutableSet.of(H1);
+            } else if (mac.equals(H2.mac())) {
+                return ImmutableSet.of(H2);
+            }
+            return ImmutableSet.of();
+        }
+
+        @Override
+        public Set<Host> getHostsByIp(IpAddress ip) {
+            if ((H1.ipAddresses().contains(ip))) {
+                return ImmutableSet.of(H1);
+            } else if ((H2.ipAddresses().contains(ip))) {
+                return ImmutableSet.of(H2);
+            }
+            return ImmutableSet.of();
+        }
+    }
+
+    private class TestLinkService extends LinkServiceAdapter {
+        @Override
+        public Set<Link> getEgressLinks(ConnectPoint connectPoint) {
+            if (connectPoint.equals(TOPO_FLOW_1_OUT_CP)
+                    || connectPoint.equals(TOPO_FLOW_OUT_CP_1)) {
+                return ImmutableSet.of(DefaultLink.builder()
+                        .providerId(ProviderId.NONE)
+                        .type(Link.Type.DIRECT)
+                        .src(connectPoint)
+                        .dst(TOPO_FLOW_2_IN_CP)
+                        .build());
+            } else if (connectPoint.equals(TOPO_FLOW_2_OUT_CP)) {
+                return ImmutableSet.of(DefaultLink.builder()
+                        .providerId(ProviderId.NONE)
+                        .type(Link.Type.DIRECT)
+                        .src(TOPO_FLOW_2_OUT_CP)
+                        .dst(TOPO_FLOW_3_IN_CP)
+                        .build());
+            } else if (connectPoint.equals(TOPO_FLOW_OUT_CP_2)) {
+                return ImmutableSet.of(DefaultLink.builder()
+                        .providerId(ProviderId.NONE)
+                        .type(Link.Type.DIRECT)
+                        .src(TOPO_FLOW_OUT_CP_2)
+                        .dst(TOPO_FLOW_4_IN_CP)
+                        .build());
+            } else if (connectPoint.equals(TOPO_FLOW_4_OUT_CP)) {
+                return ImmutableSet.of(DefaultLink.builder()
+                        .providerId(ProviderId.NONE)
+                        .type(Link.Type.DIRECT)
+                        .src(TOPO_FLOW_4_OUT_CP)
+                        .dst(TOPO_FLOW_3_IN_2_CP)
+                        .build());
+            }
+            return ImmutableSet.of();
+        }
+    }
+
+    private class TestDeviceService extends DeviceServiceAdapter {
+        @Override
+        public Device getDevice(DeviceId deviceId) {
+            if (deviceId.equals(DeviceId.deviceId("nonexistent"))) {
+                return null;
+            }
+            return new DefaultDevice(ProviderId.NONE, DeviceId.deviceId("test"), SWITCH,
+                    "test", "test", "test", "test", new ChassisId(),
+                    DefaultAnnotations.builder().set("foo", "bar").build());
+        }
+
+        @Override
+        public boolean isAvailable(DeviceId deviceId) {
+            if (deviceId.equals(OFFLINE_DEVICE)) {
+                return false;
+            }
+            return true;
+        }
+    }
+}
\ No newline at end of file
diff --git a/apps/tenbi/topology/pom.xml b/apps/tenbi/topology/pom.xml
index b3ac945..61b1453 100644
--- a/apps/tenbi/topology/pom.xml
+++ b/apps/tenbi/topology/pom.xml
@@ -62,4 +62,33 @@
         </dependency>
     </dependencies>
 
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <compilerArgs>
+                      <arg>-XepDisableAllChecks</arg>
+                      <arg>-Xep:BetaApi:OFF</arg>
+                    </compilerArgs>
+                </configuration>
+                <executions>
+                    <execution>
+                        <phase>compile</phase>
+                        <goals>
+                            <goal>compile</goal>
+                        </goals>
+                        <configuration>
+                            <compilerArgs>
+                              <arg>-XepDisableAllChecks</arg>
+                              <arg>-Xep:BetaApi:OFF</arg>
+                            </compilerArgs>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
 </project>
diff --git a/apps/tenbi/tunnel/pom.xml b/apps/tenbi/tunnel/pom.xml
index 2a64af3..2c114a7 100644
--- a/apps/tenbi/tunnel/pom.xml
+++ b/apps/tenbi/tunnel/pom.xml
@@ -90,6 +90,32 @@
                     </instructions>
                 </configuration>
             </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <compilerArgs>
+                      <arg>-XepDisableAllChecks</arg>
+                      <arg>-Xep:BetaApi:OFF</arg>
+                    </compilerArgs>
+                </configuration>
+                <executions>
+                    <execution>
+                        <phase>compile</phase>
+                        <goals>
+                            <goal>compile</goal>
+                        </goals>
+                        <configuration>
+                            <compilerArgs>
+                              <arg>-XepDisableAllChecks</arg>
+                              <arg>-Xep:BetaApi:OFF</arg>
+                            </compilerArgs>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+
         </plugins>
     </build>
 
diff --git a/apps/tenbi/utils/pom.xml b/apps/tenbi/utils/pom.xml
index 817a6da..b15a235 100644
--- a/apps/tenbi/utils/pom.xml
+++ b/apps/tenbi/utils/pom.xml
@@ -51,4 +51,34 @@
             <version>${project.version}</version>
         </dependency>
     </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <compilerArgs>
+                      <arg>-XepDisableAllChecks</arg>
+                      <arg>-Xep:BetaApi:OFF</arg>
+                    </compilerArgs>
+                </configuration>
+                <executions>
+                    <execution>
+                        <phase>compile</phase>
+                        <goals>
+                            <goal>compile</goal>
+                        </goals>
+                        <configuration>
+                            <compilerArgs>
+                              <arg>-XepDisableAllChecks</arg>
+                              <arg>-Xep:BetaApi:OFF</arg>
+                            </compilerArgs>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
 </project>
diff --git a/apps/tenbi/yangmodel/pom.xml b/apps/tenbi/yangmodel/pom.xml
index a38a441..2ad8679 100644
--- a/apps/tenbi/yangmodel/pom.xml
+++ b/apps/tenbi/yangmodel/pom.xml
@@ -44,6 +44,33 @@
 
     <build>
         <plugins>
+
+            <!-- FIXME: YANG tool generates problematic code -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <compilerArgs>
+                      <arg>-XepDisableAllChecks</arg>
+                      <arg>-Xep:BetaApi:OFF</arg>
+                    </compilerArgs>
+                </configuration>
+                <executions>
+                    <execution>
+                        <phase>compile</phase>
+                        <goals>
+                            <goal>compile</goal>
+                        </goals>
+                        <configuration>
+                            <compilerArgs>
+                              <arg>-XepDisableAllChecks</arg>
+                              <arg>-Xep:BetaApi:OFF</arg>
+                            </compilerArgs>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+
            <plugin>
                 <groupId>org.onosproject</groupId>
                 <artifactId>onos-yang-maven-plugin</artifactId>
diff --git a/apps/tetopology/app/src/main/java/org/onosproject/tetopology/management/impl/DistributedTeTopologyStore.java b/apps/tetopology/app/src/main/java/org/onosproject/tetopology/management/impl/DistributedTeTopologyStore.java
index 4a8684e..c9a0b50 100644
--- a/apps/tetopology/app/src/main/java/org/onosproject/tetopology/management/impl/DistributedTeTopologyStore.java
+++ b/apps/tetopology/app/src/main/java/org/onosproject/tetopology/management/impl/DistributedTeTopologyStore.java
@@ -1100,7 +1100,7 @@
         InternalNetwork intNework = networkMap.get(nodeKey.networkId());
         if (intNework != null && CollectionUtils.isNotEmpty(intNework.nodeIds())) {
             intNework.setChildUpdate(true);
-            intNework.nodeIds().remove(nodeKey.nodeId());
+            intNework.nodeIds().remove(nodeKey);
         }
         InternalNetworkNode intNode = networkNodeMap.remove(nodeKey);
         if (intNode != null && CollectionUtils.isNotEmpty(intNode.tpIds())) {
@@ -1287,7 +1287,7 @@
         InternalNetwork intNework = networkMap.get(linkKey.networkId());
         if (intNework != null && CollectionUtils.isNotEmpty(intNework.linkIds())) {
             intNework.setChildUpdate(true);
-            intNework.linkIds().remove(linkKey.linkId());
+            intNework.linkIds().remove(linkKey);
         }
         // Remove it from networkLinkMap
         InternalNetworkLink intLink = networkLinkMap.remove(linkKey);
@@ -1316,7 +1316,7 @@
         TeNodeKey myTeNodeKey;
         InternalNetworkNode intNode = null;
         if (!parentUpdate) {
-            intNode = networkNodeMap.get(tpKey.nodeId());
+            intNode = networkNodeMap.get(tpKey);
             if (intNode == null) {
                 log.error(" node is not in dataStore for tp update {}", tpKey);
                 return;
@@ -1351,7 +1351,7 @@
     @Override
     public void removeTerminationPoint(TerminationPointKey tpKey) {
         // Update InternalNetworkNode
-        InternalNetworkNode intNode = networkNodeMap.get(tpKey.nodeId());
+        InternalNetworkNode intNode = networkNodeMap.get(tpKey);
         if (intNode != null && CollectionUtils.isNotEmpty(intNode.tpIds())) {
             intNode.setChildUpdate(true);
             intNode.tpIds().remove(tpKey.tpId());
diff --git a/apps/tetopology/app/src/main/java/org/onosproject/tetopology/management/impl/TeTopologyManager.java b/apps/tetopology/app/src/main/java/org/onosproject/tetopology/management/impl/TeTopologyManager.java
index a0cb597..5ce27e3 100644
--- a/apps/tetopology/app/src/main/java/org/onosproject/tetopology/management/impl/TeTopologyManager.java
+++ b/apps/tetopology/app/src/main/java/org/onosproject/tetopology/management/impl/TeTopologyManager.java
@@ -916,7 +916,7 @@
 
         if (newKey != null) {
             DefaultTeTopology newTopology = new DefaultTeTopology(
-                    newKey == null ? teTopology.teTopologyId() : newKey,
+                    newKey,
                     teTopology.teNodes(), teTopology.teLinks(),
                     teTopology.teTopologyIdStringValue(), new CommonTopologyData(teTopology));
             // Update with new data
diff --git a/apps/tetopology/app/src/test/java/org/onosproject/tetopology/management/DefaultBuilder.java b/apps/tetopology/app/src/test/java/org/onosproject/tetopology/management/DefaultBuilder.java
index 47cdcd6..1c82fcc 100644
--- a/apps/tetopology/app/src/test/java/org/onosproject/tetopology/management/DefaultBuilder.java
+++ b/apps/tetopology/app/src/test/java/org/onosproject/tetopology/management/DefaultBuilder.java
@@ -143,7 +143,7 @@
         for (long i = 0; i < numTtps; i++) {
             ttps.put(i, ttpBuilder(i));
         }
-        ttp = ttps.get(FIRST_INDEX);
+        ttp = ttps.get((long) FIRST_INDEX);
         //TeNode
         teNode = new DefaultTeNode(teNodeId, underlayTopologyId,
                                    supportTeNodeId, sourceTeNodeId,
diff --git a/apps/tetopology/app/src/test/java/org/onosproject/tetopology/management/SimpleTeTopologyStore.java b/apps/tetopology/app/src/test/java/org/onosproject/tetopology/management/SimpleTeTopologyStore.java
index bc1c818..7f010fa 100644
--- a/apps/tetopology/app/src/test/java/org/onosproject/tetopology/management/SimpleTeTopologyStore.java
+++ b/apps/tetopology/app/src/test/java/org/onosproject/tetopology/management/SimpleTeTopologyStore.java
@@ -697,7 +697,7 @@
         if (intNework != null
                 && CollectionUtils.isNotEmpty(intNework.nodeIds())) {
             intNework.setChildUpdate(true);
-            intNework.nodeIds().remove(nodeKey.nodeId());
+            intNework.nodeIds().remove(nodeKey);
         }
         InternalNetworkNode intNode = networkNodeMap.remove(nodeKey);
         if (intNode != null && CollectionUtils.isNotEmpty(intNode.tpIds())) {
@@ -905,7 +905,7 @@
         if (intNework != null
                 && CollectionUtils.isNotEmpty(intNework.linkIds())) {
             intNework.setChildUpdate(true);
-            intNework.linkIds().remove(linkKey.linkId());
+            intNework.linkIds().remove(linkKey);
         }
         // Remove it from networkLinkMap
         InternalNetworkLink intLink = networkLinkMap.remove(linkKey);
@@ -937,7 +937,7 @@
         TeNodeKey myTeNodeKey;
         InternalNetworkNode intNode = null;
         if (!parentUpdate) {
-            intNode = networkNodeMap.get(tpKey.nodeId());
+            intNode = networkNodeMap.get(tpKey);
             if (intNode == null) {
                 log.error(" node is not in dataStore for tp update {}", tpKey);
                 return;
@@ -974,7 +974,7 @@
     @Override
     public void removeTerminationPoint(TerminationPointKey tpKey) {
         // Update InternalNetworkNode
-        InternalNetworkNode intNode = networkNodeMap.get(tpKey.nodeId());
+        InternalNetworkNode intNode = networkNodeMap.get(tpKey);
         if (intNode != null && CollectionUtils.isNotEmpty(intNode.tpIds())) {
             intNode.setChildUpdate(true);
             intNode.tpIds().remove(tpKey.tpId());
diff --git a/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngConfigurationManager.java b/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngConfigurationManager.java
index dded6bd..1e8a5ed 100644
--- a/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngConfigurationManager.java
+++ b/apps/virtualbng/src/main/java/org/onosproject/virtualbng/VbngConfigurationManager.java
@@ -280,10 +280,10 @@
                 return true;
             }
         }
-        if (!isPublicIpExist) {
-            log.info("The public IP address {} retrieved from XOS mapping does "
-                    + "not exist", publicIpAddress);
-        }
+
+        log.info("The public IP address {} retrieved from XOS mapping does "
+                + "not exist", publicIpAddress);
+
         return false;
     }
 
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/cli/VplsCommand.java b/apps/vpls/src/main/java/org/onosproject/vpls/cli/VplsCommand.java
index 35caa97..72489b1 100644
--- a/apps/vpls/src/main/java/org/onosproject/vpls/cli/VplsCommand.java
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/cli/VplsCommand.java
@@ -101,8 +101,8 @@
             COLOR_ERROR + "Interface " + BOLD + "%s" + RESET + COLOR_ERROR +
                     " cannot be removed from VPLS " + BOLD + "%s" + RESET + ".";
 
-    protected static Vpls vpls;
-    protected static InterfaceService interfaceService;
+    protected Vpls vpls;
+    protected InterfaceService interfaceService;
 
     @Argument(index = 0, name = "command", description = "Command name (add-if|" +
             "create|delete|list|rem-if|set-encap|show)",
@@ -377,7 +377,7 @@
      * @return true if the interface is already associated to a VPLS; false
      * otherwise
      */
-    private static boolean isIfaceAssociated(Interface iface) {
+    private boolean isIfaceAssociated(Interface iface) {
         return vpls.getAllVpls()
                 .stream()
                 .map(VplsData::interfaces)
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/cli/completer/VplsOptArgCompleter.java b/apps/vpls/src/main/java/org/onosproject/vpls/cli/completer/VplsOptArgCompleter.java
index 0624005..4b5ec46 100644
--- a/apps/vpls/src/main/java/org/onosproject/vpls/cli/completer/VplsOptArgCompleter.java
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/cli/completer/VplsOptArgCompleter.java
@@ -38,8 +38,8 @@
  * VPLS optional argument completer.
  */
 public class VplsOptArgCompleter extends AbstractChoicesCompleter {
-    protected static Vpls vpls;
-    protected static InterfaceService interfaceService;
+    protected Vpls vpls;
+    protected InterfaceService interfaceService;
 
     @Override
     public List<String> choices() {
diff --git a/apps/vpls/src/test/java/org/onosproject/vpls/VplsTest.java b/apps/vpls/src/test/java/org/onosproject/vpls/VplsTest.java
index 2e42254..10bd51c 100644
--- a/apps/vpls/src/test/java/org/onosproject/vpls/VplsTest.java
+++ b/apps/vpls/src/test/java/org/onosproject/vpls/VplsTest.java
@@ -52,6 +52,7 @@
 import org.onosproject.net.host.HostEvent;
 import org.onosproject.net.host.HostListener;
 import org.onosproject.net.host.HostServiceAdapter;
+import org.onosproject.net.host.InterfaceIpAddress;
 import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentData;
 import org.onosproject.net.intent.IntentEvent;
@@ -396,7 +397,11 @@
         @Override
         public Set<Interface> getInterfacesByIp(IpAddress ip) {
             return AVAILABLE_INTERFACES.stream()
-                    .filter(intf -> intf.ipAddressesList().contains(ip))
+                    .filter(intf -> intf.ipAddressesList().stream()
+                                .map(InterfaceIpAddress::ipAddress)
+                                .filter(ip::equals)
+                                .findAny()
+                                .isPresent())
                     .collect(Collectors.toSet());
         }
 
@@ -410,7 +415,11 @@
         @Override
         public Interface getMatchingInterface(IpAddress ip) {
             return AVAILABLE_INTERFACES.stream()
-                    .filter(intf -> intf.ipAddressesList().contains(ip))
+                    .filter(intf -> intf.ipAddressesList().stream()
+                            .map(InterfaceIpAddress::ipAddress)
+                            .filter(ip::equals)
+                            .findAny()
+                            .isPresent())
                     .findFirst()
                     .orElse(null);
         }
@@ -418,7 +427,11 @@
         @Override
         public Set<Interface> getMatchingInterfaces(IpAddress ip) {
             return AVAILABLE_INTERFACES.stream()
-                    .filter(intf -> intf.ipAddressesList().contains(ip))
+                    .filter(intf -> intf.ipAddressesList().stream()
+                            .map(InterfaceIpAddress::ipAddress)
+                            .filter(ip::equals)
+                            .findAny()
+                            .isPresent())
                     .collect(Collectors.toSet());
         }
     }
diff --git a/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/manager/impl/VtnManager.java b/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/manager/impl/VtnManager.java
index fb1bafc..b1e2f94 100644
--- a/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/manager/impl/VtnManager.java
+++ b/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/manager/impl/VtnManager.java
@@ -1121,7 +1121,7 @@
                     break;
                 }
             }
-            if (host != null && vmPort != null && fipPort != null) {
+            if (host != null && fipPort != null) {
                 DeviceId deviceId = host.location().deviceId();
                 Port exPort = exPortOfDevice.get(deviceId);
                 TenantRouter tenantRouter = TenantRouter
@@ -1205,8 +1205,7 @@
                     .programExternalOut(deviceId, fipNetwork.segmentationId(),
                                         exPort.number(), exPortMac, operation);
         } else if (operation == Objective.Operation.REMOVE) {
-            if (hostFlag || (!hostFlag
-                    && routerInfFlagOfTenantRouter.get(tenantRouter) == null)) {
+            if (hostFlag || (routerInfFlagOfTenantRouter.get(tenantRouter) == null)) {
                 sendNorthSouthL3Flows(deviceId, floatingIp, dstVmGwIp, dstVmGwMac,
                                       l3vni, vmNetwork, vmPort, host, operation);
             }
diff --git a/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultRouter.java b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultRouter.java
index f31aed1..fc8bbe5 100644
--- a/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultRouter.java
+++ b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultRouter.java
@@ -55,9 +55,9 @@
                          List<String> routes) {
         this.id = checkNotNull(id, "id cannot be null");
         this.name = routerName;
-        this.adminStateUp = checkNotNull(adminStateUp, "adminStateUp cannot be null");
+        this.adminStateUp = adminStateUp;
         this.status = checkNotNull(status, "status cannot be null");
-        this.distributed = checkNotNull(distributed, "distributed cannot be null");
+        this.distributed = distributed;
         this.externalGatewayInfo = externalGatewayInfo;
         this.gatewayPortId = gatewayPortId;
         this.tenantId = checkNotNull(tenantId, "tenantId cannot be null");
diff --git a/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/RouterGateway.java b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/RouterGateway.java
index 27e1473..6aef24e 100644
--- a/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/RouterGateway.java
+++ b/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/RouterGateway.java
@@ -35,7 +35,7 @@
     private RouterGateway(TenantNetworkId networkId, boolean enableSnat,
                          Set<FixedIp> externalFixedIps) {
         this.networkId = checkNotNull(networkId, "networkId cannot be null");
-        this.enableSnat = checkNotNull(enableSnat, "enableSnat cannot be null");
+        this.enableSnat = enableSnat;
         this.externalFixedIps = checkNotNull(externalFixedIps, "externalFixedIps cannot be null");
     }
 
diff --git a/apps/vtn/vtnrsc/src/test/java/org/onosproject/vtnrsc/util/VtnEventuallyConsistentMapTest.java b/apps/vtn/vtnrsc/src/test/java/org/onosproject/vtnrsc/util/VtnEventuallyConsistentMapTest.java
index b95b0be..38ceb0b 100644
--- a/apps/vtn/vtnrsc/src/test/java/org/onosproject/vtnrsc/util/VtnEventuallyConsistentMapTest.java
+++ b/apps/vtn/vtnrsc/src/test/java/org/onosproject/vtnrsc/util/VtnEventuallyConsistentMapTest.java
@@ -89,6 +89,7 @@
         return map.get(key);
     }
 
+    @SuppressWarnings("ReturnValueIgnored")
     @Override
     public void put(K key, V value) {
         map.put(key, value);
diff --git a/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/ClassifierResourceTest.java b/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/ClassifierResourceTest.java
index 2307da4..5fa977c 100644
--- a/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/ClassifierResourceTest.java
+++ b/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/ClassifierResourceTest.java
@@ -23,7 +23,6 @@
 import org.junit.Test;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
-import org.onlab.rest.BaseResource;
 import org.onosproject.codec.CodecService;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
@@ -57,7 +56,7 @@
         SfcCodecContext context = new SfcCodecContext();
         ServiceDirectory testDirectory = new TestServiceDirectory().add(ClassifierService.class, classifierService)
                 .add(CodecService.class, context.codecManager());
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
 
     }
 
diff --git a/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/FlowClassifierResourceTest.java b/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/FlowClassifierResourceTest.java
index f98605e..da9d6d8 100644
--- a/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/FlowClassifierResourceTest.java
+++ b/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/FlowClassifierResourceTest.java
@@ -23,7 +23,6 @@
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
 import org.onlab.packet.IpPrefix;
-import org.onlab.rest.BaseResource;
 import org.onosproject.codec.CodecService;
 import org.onosproject.vtnrsc.FlowClassifier;
 import org.onosproject.vtnrsc.FlowClassifierId;
@@ -208,7 +207,7 @@
         ServiceDirectory testDirectory = new TestServiceDirectory()
         .add(FlowClassifierService.class, flowClassifierService)
         .add(CodecService.class, context.codecManager());
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
 
     }
 
diff --git a/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortChainDeviceMapResourceTest.java b/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortChainDeviceMapResourceTest.java
index c4e219b..15c2104 100644
--- a/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortChainDeviceMapResourceTest.java
+++ b/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortChainDeviceMapResourceTest.java
@@ -15,27 +15,15 @@
  */
 package org.onosproject.vtnweb.resources;
 
-import static org.easymock.EasyMock.anyObject;
-import static org.easymock.EasyMock.createMock;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.replay;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.notNullValue;
-import static org.junit.Assert.assertThat;
-
-import java.util.HashSet;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-
-import javax.ws.rs.client.WebTarget;
-
+import com.eclipsesource.json.Json;
+import com.eclipsesource.json.JsonObject;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
-import org.onlab.rest.BaseResource;
 import org.onosproject.codec.CodecService;
 import org.onosproject.net.DeviceId;
 import org.onosproject.vtnrsc.FiveTuple;
@@ -49,10 +37,19 @@
 import org.onosproject.vtnrsc.portchain.PortChainService;
 import org.onosproject.vtnweb.web.SfcCodecContext;
 
-import com.eclipsesource.json.Json;
-import com.eclipsesource.json.JsonObject;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
+import javax.ws.rs.client.WebTarget;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertThat;
 
 /**
  * Unit tests for port chain device map REST APIs.
@@ -223,7 +220,7 @@
         ServiceDirectory testDirectory = new TestServiceDirectory()
         .add(PortChainService.class, portChainService)
         .add(CodecService.class, context.codecManager());
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
     }
 
     /**
diff --git a/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortChainResourceTest.java b/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortChainResourceTest.java
index 4fbc63f..dd81814 100644
--- a/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortChainResourceTest.java
+++ b/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortChainResourceTest.java
@@ -15,35 +15,15 @@
  */
 package org.onosproject.vtnweb.resources;
 
-import static org.easymock.EasyMock.anyObject;
-import static org.easymock.EasyMock.createMock;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.replay;
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.notNullValue;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.fail;
-
-import java.io.InputStream;
-import java.net.HttpURLConnection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-
-import javax.ws.rs.NotFoundException;
-import javax.ws.rs.client.Entity;
-import javax.ws.rs.client.WebTarget;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-
+import com.eclipsesource.json.Json;
+import com.eclipsesource.json.JsonObject;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
-import org.onlab.rest.BaseResource;
 import org.onosproject.codec.CodecService;
 import org.onosproject.net.DeviceId;
 import org.onosproject.vtnrsc.FiveTuple;
@@ -57,10 +37,27 @@
 import org.onosproject.vtnrsc.portchain.PortChainService;
 import org.onosproject.vtnweb.web.SfcCodecContext;
 
-import com.eclipsesource.json.Json;
-import com.eclipsesource.json.JsonObject;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
 
 /**
  * Unit tests for port chain REST APIs.
@@ -221,7 +218,7 @@
         ServiceDirectory testDirectory = new TestServiceDirectory()
         .add(PortChainService.class, portChainService)
         .add(CodecService.class, context.codecManager());
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
 
     }
 
diff --git a/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortChainSfMapResourceTest.java b/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortChainSfMapResourceTest.java
index d8983b0..e3866ce 100644
--- a/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortChainSfMapResourceTest.java
+++ b/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortChainSfMapResourceTest.java
@@ -15,6 +15,25 @@
  */
 package org.onosproject.vtnweb.resources;
 
+import com.eclipsesource.json.Json;
+import com.eclipsesource.json.JsonObject;
+import com.google.common.collect.Lists;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onosproject.codec.CodecService;
+import org.onosproject.vtnrsc.PortPairId;
+import org.onosproject.vtnrsc.ServiceFunctionGroup;
+import org.onosproject.vtnrsc.portchainsfmap.PortChainSfMapService;
+import org.onosproject.vtnweb.web.SfcCodecContext;
+
+import javax.ws.rs.client.WebTarget;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
 import static org.easymock.EasyMock.anyObject;
 import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.expect;
@@ -23,28 +42,6 @@
 import static org.hamcrest.Matchers.notNullValue;
 import static org.junit.Assert.assertThat;
 
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-import javax.ws.rs.client.WebTarget;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.onlab.osgi.ServiceDirectory;
-import org.onlab.osgi.TestServiceDirectory;
-import org.onlab.rest.BaseResource;
-import org.onosproject.codec.CodecService;
-import org.onosproject.vtnrsc.PortPairId;
-import org.onosproject.vtnrsc.ServiceFunctionGroup;
-import org.onosproject.vtnrsc.portchainsfmap.PortChainSfMapService;
-import org.onosproject.vtnweb.web.SfcCodecContext;
-
-import com.eclipsesource.json.Json;
-import com.eclipsesource.json.JsonObject;
-import com.google.common.collect.Lists;
-
 /**
  * Unit tests for port chain sf map REST APIs.
  */
@@ -68,7 +65,7 @@
         ServiceDirectory testDirectory = new TestServiceDirectory()
                 .add(PortChainSfMapService.class, portChainSfMapService)
                 .add(CodecService.class, context.codecManager());
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
     }
 
     /**
diff --git a/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortPairGroupResourceTest.java b/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortPairGroupResourceTest.java
index c781c88..a5f2150 100644
--- a/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortPairGroupResourceTest.java
+++ b/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortPairGroupResourceTest.java
@@ -19,13 +19,11 @@
 import com.eclipsesource.json.JsonObject;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
-import java.util.Map;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
-import org.onlab.rest.BaseResource;
 import org.onosproject.codec.CodecService;
 import org.onosproject.vtnrsc.PortPairGroup;
 import org.onosproject.vtnrsc.PortPairGroupId;
@@ -43,6 +41,7 @@
 import java.net.HttpURLConnection;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 
@@ -151,7 +150,7 @@
         ServiceDirectory testDirectory = new TestServiceDirectory()
         .add(PortPairGroupService.class, portPairGroupService)
         .add(CodecService.class, context.codecManager());
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
 
     }
 
diff --git a/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortPairResourceTest.java b/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortPairResourceTest.java
index 7f73b83..63e89b9 100644
--- a/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortPairResourceTest.java
+++ b/apps/vtn/vtnweb/src/test/java/org/onosproject/vtnweb/resources/PortPairResourceTest.java
@@ -22,7 +22,6 @@
 import org.junit.Test;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
-import org.onlab.rest.BaseResource;
 import org.onosproject.codec.CodecService;
 import org.onosproject.vtnrsc.PortPair;
 import org.onosproject.vtnrsc.PortPairId;
@@ -135,7 +134,7 @@
         SfcCodecContext context = new SfcCodecContext();
         ServiceDirectory testDirectory = new TestServiceDirectory().add(PortPairService.class, portPairService)
                 .add(CodecService.class, context.codecManager());
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
 
     }
 
diff --git a/apps/yms/app/pom.xml b/apps/yms/app/pom.xml
index 08a3713..e65758d 100644
--- a/apps/yms/app/pom.xml
+++ b/apps/yms/app/pom.xml
@@ -66,7 +66,6 @@
             <plugin>
                 <groupId>org.apache.felix</groupId>
                 <artifactId>maven-bundle-plugin</artifactId>
-                <version>3.2.0</version>
                 <extensions>true</extensions>
                 <configuration>
                     <instructions>
@@ -79,6 +78,32 @@
                     </instructions>
                 </configuration>
             </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <compilerArgs>
+                      <arg>-XepDisableAllChecks</arg>
+                      <arg>-Xep:BetaApi:OFF</arg>
+                    </compilerArgs>
+                </configuration>
+                <executions>
+                    <execution>
+                        <phase>compile</phase>
+                        <goals>
+                            <goal>compile</goal>
+                        </goals>
+                        <configuration>
+                            <compilerArgs>
+                              <arg>-XepDisableAllChecks</arg>
+                              <arg>-Xep:BetaApi:OFF</arg>
+                            </compilerArgs>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+
         </plugins>
     </build>
 </project>
diff --git a/apps/yms/ut/pom.xml b/apps/yms/ut/pom.xml
index a89438b..ca2f681 100644
--- a/apps/yms/ut/pom.xml
+++ b/apps/yms/ut/pom.xml
@@ -115,6 +115,31 @@
                 </executions>
             </plugin>
 
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <compilerArgs>
+                      <arg>-XepDisableAllChecks</arg>
+                      <arg>-Xep:BetaApi:OFF</arg>
+                    </compilerArgs>
+                </configuration>
+                <executions>
+                    <execution>
+                        <phase>compile</phase>
+                        <goals>
+                            <goal>compile</goal>
+                        </goals>
+                        <configuration>
+                            <compilerArgs>
+                              <arg>-XepDisableAllChecks</arg>
+                              <arg>-Xep:BetaApi:OFF</arg>
+                            </compilerArgs>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+
         </plugins>
     </build>
 </project>
diff --git a/buck-tools/default.defs b/buck-tools/default.defs
index ad9f8aa..0e3ff13 100644
--- a/buck-tools/default.defs
+++ b/buck-tools/default.defs
@@ -5,6 +5,7 @@
 include_defs('//bucklets/yang.bucklet')
 include_defs('//bucklets/remote_jar.bucklet')
 include_defs('//bucklets/grpc.bucklet')
+include_defs('//bucklets/jaxb2.bucklet')
 
 BASE_DEPS = [
     '//lib:junit',
diff --git a/bucklets/jaxb2.bucklet b/bucklets/jaxb2.bucklet
new file mode 100644
index 0000000..f3bb9fb
--- /dev/null
+++ b/bucklets/jaxb2.bucklet
@@ -0,0 +1,64 @@
+#
+# 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.
+#
+# Bucklet to create Java Code from XSD using the 'xjc' tool from the JAXB API v2.
+# The genrule() creates the Java code, zips it and puts it in the output of jaxb2-xjc
+# The osgi_jar_with_tests() takes the sources in this ZIP and adds them to what ever
+# sources were used in the call to this method, and compiles all of them and adds
+# them to a JAR file in the output folder
+
+include_defs('//onos.defs')
+include_defs('//bucklets/onos.bucklet')
+
+def _get_name():
+    base_path = get_base_path()
+    return ONOS_ARTIFACT_BASE + base_path.replace('/', '-') #TODO Unix-separator
+
+def jaxb2_xjc_osgi_jar(
+    name,
+    srcs,
+    xsd,
+    bindinfo=None,
+    destdir='.',
+    deps=[],
+    test_deps=[],
+    visibility = [],
+    **kwargs
+    ):
+    if name is None:
+          name = _get_name()
+
+    cmd = 'xjc '+xsd
+    if bindinfo is not None:
+        cmd=cmd+' -b '+bindinfo
+        cmd=cmd+' -d '+destdir
+
+    genrule(
+        name = 'jaxb2-xjc',
+        srcs = glob(['src/main/resources/*.xsd','src/main/resources/*.xjb']),
+        bash = cmd + ' && zip $OUT -r *',
+        out = name+'.src.zip',
+        visibility = [ ],
+    )
+
+    osgi_jar_with_tests (
+        deps = [":jaxb2-xjc"] + deps,
+        srcs = [':jaxb2-xjc'] + srcs,
+        test_deps = test_deps,
+        do_javadocs = False,
+        do_checkstyle = False,
+        **kwargs
+    )
+
diff --git a/cli/src/main/java/org/onosproject/cli/AbstractShellCommand.java b/cli/src/main/java/org/onosproject/cli/AbstractShellCommand.java
index 444631e..f83c5fa 100644
--- a/cli/src/main/java/org/onosproject/cli/AbstractShellCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/AbstractShellCommand.java
@@ -91,7 +91,8 @@
      */
     public static String annotations(Annotations annotations) {
         StringBuilder sb = new StringBuilder();
-        for (String key : annotations.keys()) {
+        Set<String> keys = new TreeSet<>(annotations.keys());
+        for (String key : keys) {
             sb.append(", ").append(key).append('=').append(annotations.value(key));
         }
         return sb.toString();
diff --git a/cli/src/main/java/org/onosproject/cli/net/FlowsListCommand.java b/cli/src/main/java/org/onosproject/cli/net/FlowsListCommand.java
index ec6178b..331431a 100644
--- a/cli/src/main/java/org/onosproject/cli/net/FlowsListCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/FlowsListCommand.java
@@ -39,6 +39,7 @@
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -303,7 +304,7 @@
                 print(LONG_FORMAT, Long.toHexString(f.id().value()), f.state(),
                         f.bytes(), f.packets(), f.life(), f.liveType(), f.priority(), f.table(),
                         appId != null ? appId.name() : "<none>",
-                        f.payLoad() == null ? null : f.payLoad().payLoad().toString(),
+                        f.payLoad() == null ? null : Arrays.toString(f.payLoad().payLoad()),
                         f.selector().criteria(), f.treatment());
             }
         }
diff --git a/cli/src/main/java/org/onosproject/cli/net/IntentRemoveCommand.java b/cli/src/main/java/org/onosproject/cli/net/IntentRemoveCommand.java
index 4ead980..cadae7e 100644
--- a/cli/src/main/java/org/onosproject/cli/net/IntentRemoveCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/IntentRemoveCommand.java
@@ -214,7 +214,7 @@
                     if (event.type() == IntentEvent.Type.WITHDRAWN ||
                             event.type() == IntentEvent.Type.FAILED) {
                         withdrawLatch.countDown();
-                    } else if (purgeAfterRemove &&
+                    } else if (purgeLatch != null && purgeAfterRemove &&
                             event.type() == IntentEvent.Type.PURGED) {
                         purgeLatch.countDown();
                     }
@@ -229,13 +229,13 @@
         // request the withdraw
         intentService.withdraw(intent);
 
-        if (purgeAfterRemove || sync) {
+        if (withdrawLatch != null && (purgeAfterRemove || sync)) {
             try { // wait for withdraw event
                 withdrawLatch.await(5, TimeUnit.SECONDS);
             } catch (InterruptedException e) {
                 print("Timed out waiting for intent {} withdraw", key);
             }
-            if (purgeAfterRemove && CAN_PURGE.contains(intentService.getIntentState(key))) {
+            if (purgeLatch != null && purgeAfterRemove && CAN_PURGE.contains(intentService.getIntentState(key))) {
                 intentService.purge(intent);
                 if (sync) { // wait for purge event
                     /* TODO
diff --git a/cli/src/main/java/org/onosproject/cli/net/IntentsListCommand.java b/cli/src/main/java/org/onosproject/cli/net/IntentsListCommand.java
index fa504df..8fb3714 100644
--- a/cli/src/main/java/org/onosproject/cli/net/IntentsListCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/IntentsListCommand.java
@@ -553,8 +553,9 @@
             builder.append('\n').append(format("bidirectional=%s", ci.isBidirectional()));
         }
 
-        List<Intent> installable = service.getInstallableIntents(intent.key());
-        installable.stream().filter(i -> contentFilter.filter(i));
+        List<Intent> installable = service.getInstallableIntents(intent.key())
+            .stream().filter(i -> contentFilter.filter(i))
+            .collect(Collectors.toList());
         if (showInstallable && installable != null && !installable.isEmpty()) {
             builder.append('\n').append(format(INSTALLABLE, installable));
         }
diff --git a/cli/src/main/java/org/onosproject/cli/net/vnet/VirtualFlowsListCommand.java b/cli/src/main/java/org/onosproject/cli/net/vnet/VirtualFlowsListCommand.java
index 8959389..ff0cf7d 100644
--- a/cli/src/main/java/org/onosproject/cli/net/vnet/VirtualFlowsListCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/vnet/VirtualFlowsListCommand.java
@@ -38,6 +38,7 @@
 import org.onosproject.utils.Comparators;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -237,7 +238,7 @@
                 print(LONG_FORMAT, Long.toHexString(f.id().value()), f.state(),
                         f.bytes(), f.packets(), f.life(), f.liveType(), f.priority(), f.tableId(),
                         appId != null ? appId.name() : "<none>",
-                        f.payLoad() == null ? null : f.payLoad().payLoad().toString(),
+                        f.payLoad() == null ? null : Arrays.toString(f.payLoad().payLoad()),
                         f.selector().criteria(), f.treatment());
             }
         }
diff --git a/cli/src/main/java/org/onosproject/cli/net/vnet/VirtualNetworkIntentRemoveCommand.java b/cli/src/main/java/org/onosproject/cli/net/vnet/VirtualNetworkIntentRemoveCommand.java
index 8f073e6..7ba6791 100644
--- a/cli/src/main/java/org/onosproject/cli/net/vnet/VirtualNetworkIntentRemoveCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/vnet/VirtualNetworkIntentRemoveCommand.java
@@ -140,7 +140,7 @@
                     if (event.type() == IntentEvent.Type.WITHDRAWN ||
                             event.type() == IntentEvent.Type.FAILED) {
                         withdrawLatch.countDown();
-                    } else if (purgeAfterRemove &&
+                    } else if (purgeLatch != null && purgeAfterRemove &&
                             event.type() == IntentEvent.Type.PURGED) {
                         purgeLatch.countDown();
                     }
@@ -155,7 +155,7 @@
         // request the withdraw
         intentService.withdraw(intent);
 
-        if (purgeAfterRemove || sync) {
+        if ((purgeAfterRemove || sync) && purgeLatch != null) {
             try { // wait for withdraw event
                 withdrawLatch.await(5, TimeUnit.SECONDS);
             } catch (InterruptedException e) {
diff --git a/core/api/src/main/java/org/onosproject/cluster/ClusterMetadata.java b/core/api/src/main/java/org/onosproject/cluster/ClusterMetadata.java
index bd3e65e..c205345 100644
--- a/core/api/src/main/java/org/onosproject/cluster/ClusterMetadata.java
+++ b/core/api/src/main/java/org/onosproject/cluster/ClusterMetadata.java
@@ -17,6 +17,7 @@
 
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Objects;
 import java.util.Set;
 import java.util.stream.Collectors;
 
@@ -140,17 +141,19 @@
     @Override
     public boolean equals(Object object) {
 
+        if (object == null) {
+            return false;
+        }
+
         if (!ClusterMetadata.class.isInstance(object)) {
             return false;
         }
         ClusterMetadata that = (ClusterMetadata) object;
 
-        if (!this.name.equals(that.name) || this.nodes.size() != that.nodes.size()
-                || this.partitions.size() != that.partitions.size()) {
-            return false;
-        }
-
-        return Sets.symmetricDifference(this.nodes, that.nodes).isEmpty()
-                && Sets.symmetricDifference(this.partitions, that.partitions).isEmpty();
+        return Objects.equals(this.name, that.name) &&
+               Objects.equals(this.nodes.size(), that.nodes.size()) &&
+               Objects.equals(this.partitions.size(), that.partitions.size()) &&
+               Sets.symmetricDifference(this.nodes, that.nodes).isEmpty() &&
+               Sets.symmetricDifference(this.partitions, that.partitions).isEmpty();
     }
 }
diff --git a/core/api/src/main/java/org/onosproject/net/config/basics/SubjectFactories.java b/core/api/src/main/java/org/onosproject/net/config/basics/SubjectFactories.java
index 1eda703..aeaf84d 100644
--- a/core/api/src/main/java/org/onosproject/net/config/basics/SubjectFactories.java
+++ b/core/api/src/main/java/org/onosproject/net/config/basics/SubjectFactories.java
@@ -140,7 +140,7 @@
      *
      * @param service core service reference
      */
-    public static void setCoreService(CoreService service) {
+    public static synchronized void setCoreService(CoreService service) {
         coreService = service;
     }
 
diff --git a/core/api/src/main/java/org/onosproject/net/flow/criteria/UnresolvedExtensionSelector.java b/core/api/src/main/java/org/onosproject/net/flow/criteria/UnresolvedExtensionSelector.java
index 17f527b..22bf95c 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/criteria/UnresolvedExtensionSelector.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/criteria/UnresolvedExtensionSelector.java
@@ -17,7 +17,6 @@
 
 import org.onosproject.net.flow.AbstractExtension;
 import java.util.Arrays;
-import java.util.Objects;
 import static com.google.common.base.MoreObjects.toStringHelper;
 
 
@@ -57,7 +56,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(bytes);
+        return Arrays.hashCode(bytes);
     }
 
     @Override
diff --git a/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java b/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java
index 9bd7df5..33b7482 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java
@@ -101,7 +101,6 @@
      * @return set-queue instruction
      */
     public static SetQueueInstruction setQueue(final long queueId, final PortNumber port) {
-        checkNotNull(queueId, "queue ID cannot be null");
         return new SetQueueInstruction(queueId, port);
     }
 
@@ -328,7 +327,6 @@
      * @return a l3 modification
      */
     public static L3ModificationInstruction modL3ArpOp(short op) {
-        checkNotNull(op, "Arp operation cannot be null");
         return new ModArpOpInstruction(L3SubType.ARP_OP, op);
     }
 
@@ -428,7 +426,6 @@
      * @return a L2 modification
      */
     public static L2ModificationInstruction modTunnelId(long tunnelId) {
-        checkNotNull(tunnelId, "Tunnel id cannot be null");
         return new L2ModificationInstruction.ModTunnelIdInstruction(tunnelId);
     }
 
diff --git a/core/api/src/main/java/org/onosproject/net/flow/instructions/UnresolvedExtensionTreatment.java b/core/api/src/main/java/org/onosproject/net/flow/instructions/UnresolvedExtensionTreatment.java
index bbe1d33..6c4a8c2 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/instructions/UnresolvedExtensionTreatment.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/instructions/UnresolvedExtensionTreatment.java
@@ -19,7 +19,6 @@
 import com.google.common.base.MoreObjects;
 import org.onosproject.net.flow.AbstractExtension;
 import java.util.Arrays;
-import java.util.Objects;
 
 /**
  * Unresolved extension treatment.
@@ -57,7 +56,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(bytes);
+        return Arrays.hashCode(bytes);
     }
 
     @Override
diff --git a/core/api/src/main/java/org/onosproject/net/intent/Intent.java b/core/api/src/main/java/org/onosproject/net/intent/Intent.java
index 7a541e7..b7af54f 100644
--- a/core/api/src/main/java/org/onosproject/net/intent/Intent.java
+++ b/core/api/src/main/java/org/onosproject/net/intent/Intent.java
@@ -48,6 +48,7 @@
     private final ResourceGroup resourceGroup;
 
     private static IdGenerator idGenerator;
+    private static final Object ID_GENERATOR_LOCK = new Object();
 
     /**
      * Constructor for serializer.
@@ -248,8 +249,10 @@
      * @param newIdGenerator id generator
      */
     public static void bindIdGenerator(IdGenerator newIdGenerator) {
-        checkState(idGenerator == null, "Id generator is already bound.");
-        idGenerator = checkNotNull(newIdGenerator);
+        synchronized (ID_GENERATOR_LOCK) {
+            checkState(idGenerator == null, "Id generator is already bound.");
+            idGenerator = checkNotNull(newIdGenerator);
+        }
     }
 
     /**
@@ -260,8 +263,10 @@
      * @param oldIdGenerator the current id generator
      */
     public static void unbindIdGenerator(IdGenerator oldIdGenerator) {
-        if (idGenerator == oldIdGenerator) {
-            idGenerator = null;
+        synchronized (ID_GENERATOR_LOCK) {
+            if (idGenerator == oldIdGenerator) {
+                idGenerator = null;
+            }
         }
     }
 
diff --git a/core/api/src/main/java/org/onosproject/net/intent/IntentOperationContext.java b/core/api/src/main/java/org/onosproject/net/intent/IntentOperationContext.java
index 6fc3f2e..528f4ba 100644
--- a/core/api/src/main/java/org/onosproject/net/intent/IntentOperationContext.java
+++ b/core/api/src/main/java/org/onosproject/net/intent/IntentOperationContext.java
@@ -103,7 +103,7 @@
         IntentOperationContext that = (IntentOperationContext) obj;
         return Objects.equals(intentsToInstall, that.intentsToInstall) &&
                 Objects.equals(intentsToUninstall, that.intentsToUninstall) &&
-                Objects.equals(intentInstallationContext, intentInstallationContext);
+                Objects.equals(intentInstallationContext, that.intentInstallationContext);
     }
 
     @Override
diff --git a/core/api/src/main/java/org/onosproject/net/intent/util/IntentFilter.java b/core/api/src/main/java/org/onosproject/net/intent/util/IntentFilter.java
index 99b887f..24be4ef 100644
--- a/core/api/src/main/java/org/onosproject/net/intent/util/IntentFilter.java
+++ b/core/api/src/main/java/org/onosproject/net/intent/util/IntentFilter.java
@@ -132,18 +132,18 @@
 
             if (objective instanceof NextObjective) {
                 nextObjective = (DefaultNextObjective) objective;
-                continue;
-
             } else if (objective instanceof ForwardingObjective) {
                 forwardObjective = (DefaultForwardingObjective) objective;
-                FlowRule flowRule = DefaultFlowRule.builder()
+                FlowRule.Builder builder = DefaultFlowRule.builder()
                         .forDevice(deviceId)
                         .withSelector(forwardObjective.selector())
-                        .withTreatment(nextObjective.next().iterator().next())
                         .withPriority(intent.priority())
                         .fromApp(intent.appId())
-                        .makePermanent()
-                        .build();
+                        .makePermanent();
+                if (nextObjective != null) {
+                    builder.withTreatment(nextObjective.next().iterator().next());
+                }
+                FlowRule flowRule = builder.build();
                 flowEntry = getFlowEntry(flowRule);
 
                 if (flowEntry != null) {
diff --git a/core/api/src/main/java/org/onosproject/store/cluster/messaging/ClusterMessage.java b/core/api/src/main/java/org/onosproject/store/cluster/messaging/ClusterMessage.java
index 4c03699..5327d48 100644
--- a/core/api/src/main/java/org/onosproject/store/cluster/messaging/ClusterMessage.java
+++ b/core/api/src/main/java/org/onosproject/store/cluster/messaging/ClusterMessage.java
@@ -155,6 +155,6 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(sender, subject, payload);
+        return Objects.hash(sender, subject, Arrays.hashCode(payload));
     }
 }
diff --git a/core/api/src/test/java/org/onosproject/net/link/LinkProviderRegistryAdapter.java b/core/api/src/test/java/org/onosproject/net/link/LinkProviderRegistryAdapter.java
index d2cfec2..c0ee902 100644
--- a/core/api/src/test/java/org/onosproject/net/link/LinkProviderRegistryAdapter.java
+++ b/core/api/src/test/java/org/onosproject/net/link/LinkProviderRegistryAdapter.java
@@ -36,11 +36,18 @@
 
     @Override
     public void unregister(LinkProvider provider) {
+        if (providerService != null && provider.id().equals(providerService.provider().id())) {
+            providerService = null;
+        }
     }
 
     @Override
     public Set<ProviderId> getProviders() {
-        return ImmutableSet.of(providerService.provider().id());
+        if (providerService != null) {
+            return ImmutableSet.of(providerService.provider().id());
+        } else {
+            return ImmutableSet.of();
+        }
     }
 
     public LinkProviderServiceAdapter registeredProvider() {
diff --git a/core/api/src/test/java/org/onosproject/security/PermissionTest.java b/core/api/src/test/java/org/onosproject/security/PermissionTest.java
index 191d868..bdab240 100644
--- a/core/api/src/test/java/org/onosproject/security/PermissionTest.java
+++ b/core/api/src/test/java/org/onosproject/security/PermissionTest.java
@@ -17,8 +17,6 @@
 package org.onosproject.security;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertSame;
-
 import org.junit.Test;
 
 /**
@@ -70,7 +68,7 @@
     public void testEqualsObject() {
         Permission permissionA = new Permission("classname", "name", "actions");
         Permission permissionB = new Permission("classname", "name", "actions");
-        assertSame(permissionA, permissionA);
+        assertEquals(permissionA, permissionB);
         assertEquals(permissionA.getClassName(), permissionB.getClassName());
         assertEquals(permissionA.getName(), permissionB.getName());
         assertEquals(permissionA.getActions(), permissionB.getActions());
diff --git a/core/api/src/test/java/org/onosproject/store/service/DocumentPathTest.java b/core/api/src/test/java/org/onosproject/store/service/DocumentPathTest.java
index c0137c8..19c34bd 100644
--- a/core/api/src/test/java/org/onosproject/store/service/DocumentPathTest.java
+++ b/core/api/src/test/java/org/onosproject/store/service/DocumentPathTest.java
@@ -73,6 +73,7 @@
         DocumentPath path1 = exceptions("node|name", parentPath);
     }
 
+    @SuppressWarnings("SelfComparison")
     @Test
     public void comparePaths() {
         DocumentPath one = path("root");
diff --git a/core/api/src/test/java/org/onosproject/store/service/TestEventuallyConsistentMap.java b/core/api/src/test/java/org/onosproject/store/service/TestEventuallyConsistentMap.java
index 4a8dc47..280cffb 100644
--- a/core/api/src/test/java/org/onosproject/store/service/TestEventuallyConsistentMap.java
+++ b/core/api/src/test/java/org/onosproject/store/service/TestEventuallyConsistentMap.java
@@ -86,6 +86,7 @@
         return map.get(key);
     }
 
+    @SuppressWarnings("ReturnValueIgnored")
     @Override
     public void put(K key, V value) {
         map.put(key, value);
diff --git a/core/api/src/test/java/org/onosproject/store/service/WallClockTimestampTest.java b/core/api/src/test/java/org/onosproject/store/service/WallClockTimestampTest.java
index 3f02fef..8ea04fd 100644
--- a/core/api/src/test/java/org/onosproject/store/service/WallClockTimestampTest.java
+++ b/core/api/src/test/java/org/onosproject/store/service/WallClockTimestampTest.java
@@ -30,6 +30,7 @@
  */
 public class WallClockTimestampTest {
 
+    @SuppressWarnings("SelfComparison")
     @Test
     public final void testBasic() throws InterruptedException {
         WallClockTimestamp ts1 = new WallClockTimestamp();
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/AnnotationsCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/AnnotationsCodec.java
index 754e24e..a155e3b 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/AnnotationsCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/AnnotationsCodec.java
@@ -23,6 +23,9 @@
 import org.onosproject.net.DefaultAnnotations;
 import org.onosproject.net.DefaultAnnotations.Builder;
 
+import java.util.Set;
+import java.util.TreeSet;
+
 /**
  * Annotations JSON codec.
  */
@@ -31,7 +34,8 @@
     @Override
     public ObjectNode encode(Annotations annotations, CodecContext context) {
         ObjectNode result = context.mapper().createObjectNode();
-        for (String key : annotations.keys()) {
+        Set<String> keys = new TreeSet<>(annotations.keys());
+        for (String key : keys) {
             result.put(key, annotations.value(key));
         }
         return result;
diff --git a/core/common/src/main/java/org/onosproject/common/DefaultTopology.java b/core/common/src/main/java/org/onosproject/common/DefaultTopology.java
index d94c88c..4b266a8 100644
--- a/core/common/src/main/java/org/onosproject/common/DefaultTopology.java
+++ b/core/common/src/main/java/org/onosproject/common/DefaultTopology.java
@@ -119,7 +119,7 @@
      *
      * @param linkWeigher new default link-weight
      */
-    public static void setDefaultLinkWeigher(LinkWeigher linkWeigher) {
+    public static synchronized void setDefaultLinkWeigher(LinkWeigher linkWeigher) {
         log.info("Setting new default link-weight function to {}", linkWeigher);
         defaultLinkWeigher = linkWeigher;
     }
@@ -130,7 +130,7 @@
      *
      * @param graphPathSearch new default algorithm
      */
-    public static void setDefaultGraphPathSearch(
+    public static synchronized void setDefaultGraphPathSearch(
             GraphPathSearch<TopologyVertex, TopologyEdge> graphPathSearch) {
         log.info("Setting new default graph path algorithm to {}", graphPathSearch);
         defaultGraphPathSearch = graphPathSearch;
diff --git a/core/net/src/main/java/org/onosproject/net/device/impl/DeviceManager.java b/core/net/src/main/java/org/onosproject/net/device/impl/DeviceManager.java
index a0c74ca..f499d1b 100644
--- a/core/net/src/main/java/org/onosproject/net/device/impl/DeviceManager.java
+++ b/core/net/src/main/java/org/onosproject/net/device/impl/DeviceManager.java
@@ -434,6 +434,7 @@
                     // can't be master if device is not reachable
                     try {
                         if (myRole == MASTER) {
+                            log.info("Local Role {}, Marking unreachable device {} offline", MASTER, deviceId);
                             post(store.markOffline(deviceId));
                         }
                         //relinquish master role and ability to be backup.
@@ -445,10 +446,11 @@
                         log.error("Exception thrown while relinquishing role for {}", deviceId, e);
                     }
                 } else {
-                    // check if the device has master, if not, mark it offline
+                    // check if the device has master and is available to the store, if not, mark it offline
                     // only the nodes which has mastership role can mark any device offline.
+                    // This condition should never be hit unless in a device removed phase for NONE mastership roles.
                     NodeId master = mastershipService.getMasterFor(deviceId);
-                    if (master == null) {
+                    if (master == null && isAvailable(deviceId)) {
                         CompletableFuture<MastershipRole> roleFuture = mastershipService.requestRoleFor(deviceId);
                         roleFuture.thenAccept(role -> {
                             MastershipTerm term = termService.getMastershipTerm(deviceId);
diff --git a/core/net/src/main/java/org/onosproject/net/flowobjective/impl/FlowObjectiveManager.java b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/FlowObjectiveManager.java
index 09d3a24..c623bf2 100644
--- a/core/net/src/main/java/org/onosproject/net/flowobjective/impl/FlowObjectiveManager.java
+++ b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/FlowObjectiveManager.java
@@ -208,7 +208,7 @@
         public ObjectiveInstaller(DeviceId deviceId, Objective objective, int attemps) {
             this.deviceId = checkNotNull(deviceId);
             this.objective = checkNotNull(objective);
-            this.numAttempts = checkNotNull(attemps);
+            this.numAttempts = attemps;
         }
 
         @Override
diff --git a/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionManager.java b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionManager.java
index be90cb5..a765648 100644
--- a/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionManager.java
+++ b/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionManager.java
@@ -177,7 +177,7 @@
         public ObjectiveInstaller(DeviceId deviceId, Objective objective, int attemps) {
             this.deviceId = checkNotNull(deviceId);
             this.objective = checkNotNull(objective);
-            this.numAttempts = checkNotNull(attemps);
+            this.numAttempts = attemps;
         }
 
         @Override
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionCompiler.java
index 75b3eba..41e8e2c 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionCompiler.java
@@ -504,7 +504,7 @@
          * The ordering criteria is untagged. First we add the untagged
          * ports. Then the others.
          */
-        if (vlanIdCriterion == null && mplsLabelCriterion == null) {
+        if (vlanIdCriterion == null) {
             orderedList.addAll(untaggedEgressPoints);
             orderedList.addAll(vlanEgressPoints);
             orderedList.addAll(mplsEgressPoints);
diff --git a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PointToPointIntentCompiler.java b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PointToPointIntentCompiler.java
index b53143b..0c47196 100644
--- a/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PointToPointIntentCompiler.java
+++ b/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PointToPointIntentCompiler.java
@@ -589,36 +589,34 @@
                                                  PointToPointIntent pointIntent) {
         List<Intent> intentList = new ArrayList<>();
         intentList.addAll(oldInstallables);
-        erasePrimary = false;
-        eraseBackup = false;
-        if (intentList != null) {
-            Iterator<Intent> iterator = intentList.iterator();
-            while (iterator.hasNext() && !(erasePrimary && eraseBackup)) {
-                Intent intent = iterator.next();
-                intent.resources().forEach(resource -> {
-                    if (resource instanceof Link) {
-                        Link link = (Link) resource;
-                        if (link.state() == Link.State.INACTIVE) {
+
+        Iterator<Intent> iterator = intentList.iterator();
+        while (iterator.hasNext()) {
+            Intent intent = iterator.next();
+            intent.resources().forEach(resource -> {
+                if (resource instanceof Link) {
+                    Link link = (Link) resource;
+                    if (link.state() == Link.State.INACTIVE) {
+                        setPathsToRemove(intent);
+                    } else if (link instanceof EdgeLink) {
+                        ConnectPoint connectPoint = (link.src().elementId() instanceof DeviceId)
+                                ? link.src() : link.dst();
+                        Port port = deviceService.getPort(connectPoint.deviceId(), connectPoint.port());
+                        if (port == null || !port.isEnabled()) {
                             setPathsToRemove(intent);
-                        } else if (link instanceof EdgeLink) {
-                            ConnectPoint connectPoint = (link.src().elementId() instanceof DeviceId)
-                                    ? link.src() : link.dst();
-                            Port port = deviceService.getPort(connectPoint.deviceId(), connectPoint.port());
-                            if (port == null || !port.isEnabled()) {
-                                setPathsToRemove(intent);
-                            }
-                        } else {
-                            Port port1 = deviceService.getPort(link.src().deviceId(), link.src().port());
-                            Port port2 = deviceService.getPort(link.dst().deviceId(), link.dst().port());
-                            if (port1 == null || !port1.isEnabled() || port2 == null || !port2.isEnabled()) {
-                                setPathsToRemove(intent);
-                            }
+                        }
+                    } else {
+                        Port port1 = deviceService.getPort(link.src().deviceId(), link.src().port());
+                        Port port2 = deviceService.getPort(link.dst().deviceId(), link.dst().port());
+                        if (port1 == null || !port1.isEnabled() || port2 == null || !port2.isEnabled()) {
+                            setPathsToRemove(intent);
                         }
                     }
-                });
-            }
-            removeAndUpdateIntents(intentList, pointIntent);
+                }
+            });
         }
+        removeAndUpdateIntents(intentList, pointIntent);
+
         return intentList;
     }
 
diff --git a/core/net/src/main/java/org/onosproject/net/packet/impl/PacketManager.java b/core/net/src/main/java/org/onosproject/net/packet/impl/PacketManager.java
index 943bb93..657ca48 100644
--- a/core/net/src/main/java/org/onosproject/net/packet/impl/PacketManager.java
+++ b/core/net/src/main/java/org/onosproject/net/packet/impl/PacketManager.java
@@ -385,9 +385,19 @@
             // TODO filter packets sent to processors based on registrations
             for (ProcessorEntry entry : processors) {
                 try {
+                    if (log.isTraceEnabled()) {
+                        log.trace("Starting packet processing by {}",
+                                entry.processor().getClass().getName());
+                    }
+
                     long start = System.nanoTime();
                     entry.processor().process(context);
                     entry.addNanos(System.nanoTime() - start);
+
+                    if (log.isTraceEnabled()) {
+                        log.trace("Finished packet processing by {}",
+                                entry.processor().getClass().getName());
+                    }
                 } catch (Exception e) {
                     log.warn("Packet processor {} threw an exception", entry.processor(), e);
                 }
diff --git a/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorImpl.java b/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorImpl.java
index f04e7b0..430e5e1 100644
--- a/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorImpl.java
+++ b/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorImpl.java
@@ -31,6 +31,7 @@
 import org.onosproject.net.pi.model.PiActionParamModel;
 import org.onosproject.net.pi.model.PiMatchFieldId;
 import org.onosproject.net.pi.model.PiMatchFieldModel;
+import org.onosproject.net.pi.model.PiMatchType;
 import org.onosproject.net.pi.model.PiPipeconf;
 import org.onosproject.net.pi.model.PiPipelineInterpreter;
 import org.onosproject.net.pi.model.PiPipelineModel;
@@ -103,26 +104,23 @@
         // Build PI entry.
         final PiTableEntry.Builder tableEntryBuilder = PiTableEntry.builder();
 
-        // In the P4 world 0 is the highest priority, in ONOS the lowest one.
-        // FIXME: move priority conversion to the driver, where different constraints might apply
-        // e.g. less bits for encoding priority in TCAM-based implementations.
-        final int newPriority;
-        if (rule.priority() > MAX_PI_PRIORITY) {
-            log.warn("Flow rule priority too big, setting translated priority to max value {}: {}",
-                     MAX_PI_PRIORITY, rule);
-            newPriority = 0;
-        } else {
-            newPriority = MAX_PI_PRIORITY - rule.priority();
-        }
+        // FIXME: P4Runtime limit
+        // Need to ignore priority if no TCAM lookup match field
+        boolean dontIgnorePriority = fieldMatches.stream()
+                .anyMatch(match -> match.type() == PiMatchType.TERNARY ||
+                        match.type() == PiMatchType.RANGE);
 
         tableEntryBuilder
                 .forTable(piTableId)
-                .withPriority(newPriority)
                 .withMatchKey(PiMatchKey.builder()
                                       .addFieldMatches(fieldMatches)
                                       .build())
                 .withAction(piTableAction);
 
+        if (dontIgnorePriority) {
+            tableEntryBuilder.withPriority(rule.priority());
+        }
+
         if (!rule.isPermanent()) {
             if (tableModel.supportsAging()) {
                 tableEntryBuilder.withTimeout((double) rule.timeout());
diff --git a/core/net/src/test/java/org/onosproject/net/config/basics/PortAnnotationConfigTest.java b/core/net/src/test/java/org/onosproject/net/config/basics/PortAnnotationConfigTest.java
index dd3211c..df6ce7b 100644
--- a/core/net/src/test/java/org/onosproject/net/config/basics/PortAnnotationConfigTest.java
+++ b/core/net/src/test/java/org/onosproject/net/config/basics/PortAnnotationConfigTest.java
@@ -126,6 +126,7 @@
         assertThat(annotations.get(key), is(value));
     }
 
+    @Test
     public void writeEntryTest() throws JsonProcessingException, IOException {
 
         PortAnnotationConfig w = new PortAnnotationConfig();
@@ -146,6 +147,7 @@
         assertThat(annotations.get(key), is(value));
     }
 
+    @Test
     public void writeMapTest() throws JsonProcessingException, IOException {
 
         PortAnnotationConfig w = new PortAnnotationConfig();
diff --git a/core/net/src/test/java/org/onosproject/net/pi/impl/PiPipeconfManagerTest.java b/core/net/src/test/java/org/onosproject/net/pi/impl/PiPipeconfManagerTest.java
index ff3b119..b269751 100644
--- a/core/net/src/test/java/org/onosproject/net/pi/impl/PiPipeconfManagerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/pi/impl/PiPipeconfManagerTest.java
@@ -138,7 +138,7 @@
     @Test
     public void register() {
         piPipeconfService.register(piPipeconf);
-        assertTrue("PiPipeconf should be registered", piPipeconfService.piPipeconfs.contains(piPipeconf));
+        assertTrue("PiPipeconf should be registered", piPipeconfService.piPipeconfs.containsValue(piPipeconf));
     }
 
     @Test
diff --git a/core/net/src/test/java/org/onosproject/net/pi/impl/PiTranslatorServiceTest.java b/core/net/src/test/java/org/onosproject/net/pi/impl/PiTranslatorServiceTest.java
index 801780e..500f8cf 100644
--- a/core/net/src/test/java/org/onosproject/net/pi/impl/PiTranslatorServiceTest.java
+++ b/core/net/src/test/java/org/onosproject/net/pi/impl/PiTranslatorServiceTest.java
@@ -66,7 +66,6 @@
 import static org.onlab.util.ImmutableByteSequence.copyFrom;
 import static org.onlab.util.ImmutableByteSequence.fit;
 import static org.onosproject.net.group.GroupDescription.Type.SELECT;
-import static org.onosproject.net.pi.impl.PiFlowRuleTranslatorImpl.MAX_PI_PRIORITY;
 import static org.onosproject.pipelines.basic.BasicConstants.ACT_PRF_WCMP_SELECTOR_ID;
 import static org.onosproject.pipelines.basic.BasicConstants.ACT_PRM_PORT_ID;
 import static org.onosproject.pipelines.basic.BasicConstants.ACT_SET_EGRESS_PORT_ID;
@@ -199,8 +198,10 @@
                    ethTypeParam.value().asReadOnlyBuffer().getShort(), is(equalTo(ethType)));
         assertThat("Incorrect ethType match param mask",
                    ethTypeParam.mask().asReadOnlyBuffer().getShort(), is(equalTo(ETH_TYPE_MASK)));
-        assertThat("Incorrect priority value",
-                   entry1.priority().get(), is(equalTo(MAX_PI_PRIORITY - rule1.priority())));
+        // FIXME: re-enable when P4Runtime priority handling will be moved out of transltion service
+        // see PiFlowRuleTranslatorImpl
+        // assertThat("Incorrect priority value",
+        //            entry1.priority().get(), is(equalTo(MAX_PI_PRIORITY - rule1.priority())));
         assertThat("Incorrect timeout value",
                    entry1.timeout(), is(equalTo(expectedTimeout)));
 
diff --git a/core/store/dist/src/main/java/org/onosproject/store/app/DistributedApplicationStore.java b/core/store/dist/src/main/java/org/onosproject/store/app/DistributedApplicationStore.java
index ffda6f5..243c3c2 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/app/DistributedApplicationStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/app/DistributedApplicationStore.java
@@ -150,33 +150,33 @@
     @Activate
     public void activate() {
         messageHandlingExecutor = newSingleThreadExecutor(groupedThreads("onos/store/app",
-                                                                         "message-handler", log));
+                "message-handler", log));
         clusterCommunicator.addSubscriber(APP_BITS_REQUEST,
-                                          bytes -> new String(bytes, Charsets.UTF_8),
-                                          name -> {
-                                              try {
-                                                  log.info("Sending bits for application {}", name);
-                                                  return toByteArray(getApplicationInputStream(name));
-                                              } catch (IOException e) {
-                                                  throw new StorageException(e);
-                                              }
-                                          },
-                                          Function.identity(),
-                                          messageHandlingExecutor);
+                bytes -> new String(bytes, Charsets.UTF_8),
+                name -> {
+                    try {
+                        log.info("Sending bits for application {}", name);
+                        return toByteArray(getApplicationInputStream(name));
+                    } catch (IOException e) {
+                        throw new StorageException(e);
+                    }
+                },
+                Function.identity(),
+                messageHandlingExecutor);
 
         apps = storageService.<ApplicationId, InternalApplicationHolder>consistentMapBuilder()
                 .withName("onos-apps")
                 .withRelaxedReadConsistency()
                 .withSerializer(Serializer.using(KryoNamespaces.API,
-                                                 InternalApplicationHolder.class,
-                                                 InternalState.class))
+                        InternalApplicationHolder.class,
+                        InternalState.class))
                 .build();
 
         appActivationTopic = storageService.getTopic("onos-apps-activation-topic",
-                                                     Serializer.using(KryoNamespaces.API));
+                Serializer.using(KryoNamespaces.API));
 
         activationExecutor = newSingleThreadExecutor(groupedThreads("onos/store/app",
-                                                                    "app-activation", log));
+                "app-activation", log));
         appActivationTopic.subscribe(appActivator, activationExecutor);
 
         executor = newSingleThreadScheduledExecutor(groupedThreads("onos/app", "store", log));
@@ -313,10 +313,10 @@
     @Override
     public Set<Application> getApplications() {
         return ImmutableSet.copyOf(apps.values()
-                                       .stream()
-                                       .map(Versioned::value)
-                                       .map(InternalApplicationHolder::app)
-                                       .collect(Collectors.toSet()));
+                .stream()
+                .map(Versioned::value)
+                .map(InternalApplicationHolder::app)
+                .collect(Collectors.toSet()));
     }
 
     @Override
@@ -425,11 +425,11 @@
         if (requiredBy.get(appId).isEmpty()) {
             AtomicBoolean stateChanged = new AtomicBoolean(false);
             apps.computeIf(appId,
-                v -> v != null && v.state() != DEACTIVATED,
-                (k, v) -> {
-                    stateChanged.set(true);
-                    return new InternalApplicationHolder(v.app(), DEACTIVATED, v.permissions());
-                });
+                    v -> v != null && v.state() != DEACTIVATED,
+                    (k, v) -> {
+                        stateChanged.set(true);
+                        return new InternalApplicationHolder(v.app(), DEACTIVATED, v.permissions());
+                    });
             if (stateChanged.get()) {
                 updateTime(appId.name());
                 deactivateRequiredApps(appId);
@@ -440,22 +440,22 @@
     // Deactivates all apps that require this application.
     private void deactivateDependentApps(ApplicationId appId) {
         apps.values()
-            .stream()
-            .map(Versioned::value)
-            .filter(a -> a.state() == ACTIVATED)
-            .filter(a -> a.app().requiredApps().contains(appId.name()))
-            .forEach(a -> deactivate(a.app().id()));
+                .stream()
+                .map(Versioned::value)
+                .filter(a -> a.state() == ACTIVATED)
+                .filter(a -> a.app().requiredApps().contains(appId.name()))
+                .forEach(a -> deactivate(a.app().id()));
     }
 
     // Deactivates all apps required by this application.
     private void deactivateRequiredApps(ApplicationId appId) {
         getApplication(appId).requiredApps()
-                             .stream()
-                             .map(this::getId)
-                             .map(apps::get)
-                             .map(Versioned::value)
-                             .filter(a -> a.state() == ACTIVATED)
-                             .forEach(a -> deactivate(a.app().id(), appId));
+                .stream()
+                .map(this::getId)
+                .map(apps::get)
+                .map(Versioned::value)
+                .filter(a -> a.state() == ACTIVATED)
+                .forEach(a -> deactivate(a.app().id(), appId));
     }
 
     @Override
@@ -468,11 +468,11 @@
     public void setPermissions(ApplicationId appId, Set<Permission> permissions) {
         AtomicBoolean permissionsChanged = new AtomicBoolean(false);
         Versioned<InternalApplicationHolder> appHolder = apps.computeIf(appId,
-            v -> v != null && !Sets.symmetricDifference(v.permissions(), permissions).isEmpty(),
-            (k, v) -> {
-                permissionsChanged.set(true);
-                return new InternalApplicationHolder(v.app(), v.state(), ImmutableSet.copyOf(permissions));
-            });
+                v -> v != null && !Sets.symmetricDifference(v.permissions(), permissions).isEmpty(),
+                (k, v) -> {
+                    permissionsChanged.set(true);
+                    return new InternalApplicationHolder(v.app(), v.state(), ImmutableSet.copyOf(permissions));
+                });
         if (permissionsChanged.get()) {
             notifyDelegate(new ApplicationEvent(APP_PERMISSIONS_CHANGED, appHolder.value().app()));
         }
@@ -509,14 +509,18 @@
             ApplicationId appId = event.key();
             InternalApplicationHolder newApp = event.newValue() == null ? null : event.newValue().value();
             InternalApplicationHolder oldApp = event.oldValue() == null ? null : event.oldValue().value();
-            if (event.type() == MapEvent.Type.INSERT || event.type() == MapEvent.Type.UPDATE) {
-                if (event.type() == MapEvent.Type.UPDATE && newApp.state() == oldApp.state()) {
-                    return;
-                }
+            if (event.type() == MapEvent.Type.UPDATE && (newApp == null || oldApp == null ||
+                    newApp.state() == oldApp.state())) {
+                log.warn("Can't update the application {}", event.key());
+                return;
+            }
+            if ((event.type() == MapEvent.Type.INSERT || event.type() == MapEvent.Type.UPDATE) && newApp != null) {
                 setupApplicationAndNotify(appId, newApp.app(), newApp.state());
-            } else if (event.type() == MapEvent.Type.REMOVE) {
-                purgeApplication(appId.name());
+            } else if (event.type() == MapEvent.Type.REMOVE && oldApp != null) {
                 notifyDelegate(new ApplicationEvent(APP_UNINSTALLED, oldApp.app()));
+                purgeApplication(appId.name());
+            } else {
+                log.warn("Can't perform {} on application {}", event.type(), event.key());
             }
         }
     }
@@ -580,22 +584,22 @@
                 continue;
             }
             clusterCommunicator.sendAndReceive(app.id().name(),
-                                               APP_BITS_REQUEST,
-                                               s -> s.getBytes(Charsets.UTF_8),
-                                               Function.identity(),
-                                               node.id())
+                    APP_BITS_REQUEST,
+                    s -> s.getBytes(Charsets.UTF_8),
+                    Function.identity(),
+                    node.id())
                     .whenCompleteAsync((bits, error) -> {
                         if (error == null && latch.getCount() > 0) {
                             saveApplication(new ByteArrayInputStream(bits));
                             log.info("Downloaded bits for application {} from node {}",
-                                     app.id().name(), node.id());
+                                    app.id().name(), node.id());
                             latch.countDown();
                             if (delegateInstallation) {
                                 notifyDelegate(new ApplicationEvent(APP_INSTALLED, app));
                             }
                         } else if (error != null) {
                             log.warn("Unable to fetch bits for application {} from node {}",
-                                     app.id().name(), node.id());
+                                    app.id().name(), node.id());
                         }
                     }, messageHandlingExecutor);
         }
diff --git a/core/store/dist/src/main/java/org/onosproject/store/cluster/impl/DistributedClusterStore.java b/core/store/dist/src/main/java/org/onosproject/store/cluster/impl/DistributedClusterStore.java
index b106763..47e5a96 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/cluster/impl/DistributedClusterStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/cluster/impl/DistributedClusterStore.java
@@ -293,6 +293,7 @@
                 if (phi >= phiFailureThreshold) {
                     if (currentState.isActive()) {
                         updateNode(node.id(), State.INACTIVE, null);
+                        failureDetector.reset(node.id());
                     }
                 } else {
                     if (currentState == State.INACTIVE) {
@@ -334,7 +335,12 @@
         public void accept(Endpoint sender, byte[] message) {
             HeartbeatMessage hb = SERIALIZER.decode(message);
             if (clusterMetadataService.getClusterMetadata().getNodes().contains(hb.source())) {
-                failureDetector.report(hb.source().id());
+                // Avoid reporting heartbeats that have been enqueued by setting a minimum interval.
+                long heartbeatTime = System.currentTimeMillis();
+                long lastHeartbeatTime = failureDetector.getLastHeartbeatTime(hb.source().id());
+                if (heartbeatTime - lastHeartbeatTime > heartbeatInterval / 2) {
+                    failureDetector.report(hb.source().id(), heartbeatTime);
+                }
                 updateNode(hb.source().id(), hb.state, hb.version);
             }
         }
diff --git a/core/store/dist/src/main/java/org/onosproject/store/cluster/impl/PhiAccrualFailureDetector.java b/core/store/dist/src/main/java/org/onosproject/store/cluster/impl/PhiAccrualFailureDetector.java
index 590946d..b80142b 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/cluster/impl/PhiAccrualFailureDetector.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/cluster/impl/PhiAccrualFailureDetector.java
@@ -49,6 +49,17 @@
     private double bootstrapPhiValue = DEFAULT_BOOTSTRAP_PHI_VALUE;
 
     /**
+     * Returns the last heartbeat time for the given node.
+     *
+     * @param nodeId the node identifier
+     * @return the last heartbeat time for the given node
+     */
+    public long getLastHeartbeatTime(NodeId nodeId) {
+        History nodeState = states.computeIfAbsent(nodeId, key -> new History());
+        return nodeState.latestHeartbeatTime();
+    }
+
+    /**
      * Report a new heart beat for the specified node id.
      * @param nodeId node id
      */
@@ -75,7 +86,14 @@
         }
     }
 
-
+    /**
+     * Resets the failure detector for the given node.
+     *
+     * @param nodeId node identifier for the node for which to reset the failure detector
+     */
+    public void reset(NodeId nodeId) {
+        states.remove(nodeId);
+    }
 
     /**
      * Compute phi for the specified node id.
diff --git a/core/store/dist/src/main/java/org/onosproject/store/cluster/messaging/impl/NettyMessagingManager.java b/core/store/dist/src/main/java/org/onosproject/store/cluster/messaging/impl/NettyMessagingManager.java
index 50c8fc6f..a5c5f3b 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/cluster/messaging/impl/NettyMessagingManager.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/cluster/messaging/impl/NettyMessagingManager.java
@@ -49,10 +49,8 @@
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.BiConsumer;
 import java.util.function.BiFunction;
-import java.util.function.Consumer;
 import java.util.function.Function;
 
-import com.google.common.base.Throwables;
 import com.google.common.cache.Cache;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.collect.Lists;
@@ -341,40 +339,10 @@
         return Math.abs(messageType.hashCode() % CHANNEL_POOL_SIZE);
     }
 
-    private <T> CompletableFuture<T> executeOnPooledConnection(
-            Endpoint endpoint,
-            String type,
-            Function<ClientConnection, CompletableFuture<T>> callback,
-            Executor executor) {
-        CompletableFuture<T> future = new CompletableFuture<T>();
-        executeOnPooledConnection(endpoint, type, callback, executor, future);
-        return future;
-    }
-
-    private <T> void executeOnPooledConnection(
-        Endpoint endpoint,
-        String type,
-        Function<ClientConnection, CompletableFuture<T>> callback,
-        Executor executor,
-        CompletableFuture<T> future) {
-
-        // If the endpoint is the local node, avoid the loopback interface and use the singleton local connection.
-        if (endpoint.equals(localEndpoint)) {
-            callback.apply(localClientConnection).whenComplete((result, error) -> {
-                if (error == null) {
-                    executor.execute(() -> future.complete(result));
-                } else {
-                    executor.execute(() -> future.completeExceptionally(error));
-                }
-            });
-            return;
-        }
-
-        // Get the channel pool and the offset for this message type.
+    private CompletableFuture<Channel> getChannel(Endpoint endpoint, String messageType) {
         List<CompletableFuture<Channel>> channelPool = getChannelPool(endpoint);
-        int offset = getChannelOffset(type);
+        int offset = getChannelOffset(messageType);
 
-        // If the channel future is completed exceptionally, open a new channel.
         CompletableFuture<Channel> channelFuture = channelPool.get(offset);
         if (channelFuture == null || channelFuture.isCompletedExceptionally()) {
             synchronized (channelPool) {
@@ -386,29 +354,7 @@
             }
         }
 
-        // Create a consumer with which to complete the send operation on a given channel.
-        final Consumer<Channel> runner = channel -> {
-            ClientConnection connection = clientConnections.computeIfAbsent(channel, RemoteClientConnection::new);
-            callback.apply(connection).whenComplete((result, sendError) -> {
-                if (sendError == null) {
-                    executor.execute(() -> future.complete(result));
-                } else {
-                    // If an exception other than a TimeoutException occurred, close the connection and
-                    // remove the channel from the pool.
-                    if (!(Throwables.getRootCause(sendError) instanceof TimeoutException)) {
-                        synchronized (channelPool) {
-                            channelPool.set(offset, null);
-                        }
-                        connection.close();
-                        channel.close();
-                    }
-                    executor.execute(() -> future.completeExceptionally(sendError));
-                }
-            });
-        };
-
-        // Wait for the channel future to be completed. Once it's complete, if the channel is active then
-        // attempt to send the message. Otherwise, if the channel is inactive then attempt to open a new channel.
+        CompletableFuture<Channel> future = new CompletableFuture<>();
         final CompletableFuture<Channel> finalFuture = channelFuture;
         finalFuture.whenComplete((channel, error) -> {
             if (error == null) {
@@ -417,11 +363,17 @@
                         CompletableFuture<Channel> currentFuture = channelPool.get(offset);
                         if (currentFuture == finalFuture) {
                             channelPool.set(offset, null);
-                            executeOnPooledConnection(endpoint, type, callback, executor);
+                            getChannel(endpoint, messageType).whenComplete((recursiveResult, recursiveError) -> {
+                                if (recursiveError == null) {
+                                    future.complete(recursiveResult);
+                                } else {
+                                    future.completeExceptionally(recursiveError);
+                                }
+                            });
                         } else {
                             currentFuture.whenComplete((recursiveResult, recursiveError) -> {
                                 if (recursiveError == null) {
-                                    runner.accept(recursiveResult);
+                                    future.complete(recursiveResult);
                                 } else {
                                     future.completeExceptionally(recursiveError);
                                 }
@@ -429,12 +381,56 @@
                         }
                     }
                 } else {
-                    runner.accept(channel);
+                    future.complete(channel);
                 }
             } else {
                 future.completeExceptionally(error);
             }
         });
+        return future;
+    }
+
+    private <T> CompletableFuture<T> executeOnPooledConnection(
+            Endpoint endpoint,
+            String type,
+            Function<ClientConnection, CompletableFuture<T>> callback,
+            Executor executor) {
+        CompletableFuture<T> future = new CompletableFuture<T>();
+        executeOnPooledConnection(endpoint, type, callback, executor, future);
+        return future;
+    }
+
+    private <T> void executeOnPooledConnection(
+            Endpoint endpoint,
+            String type,
+            Function<ClientConnection, CompletableFuture<T>> callback,
+            Executor executor,
+            CompletableFuture<T> future) {
+        if (endpoint.equals(localEndpoint)) {
+            callback.apply(localClientConnection).whenComplete((result, error) -> {
+                if (error == null) {
+                    executor.execute(() -> future.complete(result));
+                } else {
+                    executor.execute(() -> future.completeExceptionally(error));
+                }
+            });
+            return;
+        }
+
+        getChannel(endpoint, type).whenComplete((channel, channelError) -> {
+            if (channelError == null) {
+                ClientConnection connection = clientConnections.computeIfAbsent(channel, RemoteClientConnection::new);
+                callback.apply(connection).whenComplete((result, sendError) -> {
+                    if (sendError == null) {
+                        executor.execute(() -> future.complete(result));
+                    } else {
+                        executor.execute(() -> future.completeExceptionally(sendError));
+                    }
+                });
+            } else {
+                executor.execute(() -> future.completeExceptionally(channelError));
+            }
+        });
     }
 
     @Override
diff --git a/core/store/dist/src/main/java/org/onosproject/store/device/impl/GossipDeviceStore.java b/core/store/dist/src/main/java/org/onosproject/store/device/impl/GossipDeviceStore.java
index c4fe0a8..ded0f31 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/device/impl/GossipDeviceStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/device/impl/GossipDeviceStore.java
@@ -317,7 +317,7 @@
                     ? deviceClockService.getTimestamp(deviceId)
                     : removalRequest.getOrDefault(deviceId, DEFAULT_TIMESTAMP);
         } catch (IllegalStateException e) {
-            newTimestamp = removalRequest.getOrDefault(deviceDescription, DEFAULT_TIMESTAMP);
+            newTimestamp = removalRequest.getOrDefault(deviceId, DEFAULT_TIMESTAMP);
             isMaster = false;
         }
         final Timestamped<DeviceDescription> deltaDesc = new Timestamped<>(deviceDescription, newTimestamp);
@@ -617,7 +617,7 @@
             return Collections.emptyList();
         }
 
-        return deviceEvents == null ? Collections.emptyList() : deviceEvents;
+        return deviceEvents;
     }
 
     private List<DeviceEvent> updatePortsInternal(ProviderId providerId,
diff --git a/core/store/dist/src/main/java/org/onosproject/store/flow/impl/ECFlowRuleStore.java b/core/store/dist/src/main/java/org/onosproject/store/flow/impl/ECFlowRuleStore.java
index 266e6fb..64c09cf 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/flow/impl/ECFlowRuleStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/flow/impl/ECFlowRuleStore.java
@@ -827,15 +827,16 @@
 
         public FlowEntry remove(DeviceId deviceId, FlowEntry rule) {
             final AtomicReference<FlowEntry> removedRule = new AtomicReference<>();
-            getFlowEntriesInternal(rule.deviceId(), rule.id())
-                .computeIfPresent((StoredFlowEntry) rule, (k, stored) -> {
+            final Map<FlowId, Map<StoredFlowEntry, StoredFlowEntry>> flowTable = getFlowTable(deviceId);
+            flowTable.computeIfPresent(rule.id(), (flowId, flowEntries) -> {
+                flowEntries.computeIfPresent((StoredFlowEntry) rule, (k, stored) -> {
                     if (rule instanceof DefaultFlowEntry) {
                         DefaultFlowEntry toRemove = (DefaultFlowEntry) rule;
                         if (stored instanceof DefaultFlowEntry) {
                             DefaultFlowEntry storedEntry = (DefaultFlowEntry) stored;
                             if (toRemove.created() < storedEntry.created()) {
                                 log.debug("Trying to remove more recent flow entry {} (stored: {})",
-                                          toRemove, stored);
+                                    toRemove, stored);
                                 // the key is not updated, removedRule remains null
                                 return stored;
                             }
@@ -844,6 +845,8 @@
                     removedRule.set(stored);
                     return null;
                 });
+                return flowEntries.isEmpty() ? null : flowEntries;
+            });
 
             if (removedRule.get() != null) {
                 lastUpdateTimes.put(deviceId, System.currentTimeMillis());
diff --git a/core/store/dist/src/main/java/org/onosproject/store/host/impl/DistributedHostStore.java b/core/store/dist/src/main/java/org/onosproject/store/host/impl/DistributedHostStore.java
index dcf58e6..9cf9856 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/host/impl/DistributedHostStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/host/impl/DistributedHostStore.java
@@ -477,7 +477,7 @@
             }
         }
 
-        if (existingHosts.isEmpty()) {
+        if (existingHosts == null || existingHosts.isEmpty()) {
             return null;
         }
         return existingHosts;
diff --git a/core/store/dist/src/main/java/org/onosproject/store/resource/impl/ConsistentResourceStore.java b/core/store/dist/src/main/java/org/onosproject/store/resource/impl/ConsistentResourceStore.java
index 14ed0ed..e5ed75b 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/resource/impl/ConsistentResourceStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/resource/impl/ConsistentResourceStore.java
@@ -63,6 +63,7 @@
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.stream.Collectors.groupingBy;
 import static org.onosproject.net.resource.ResourceEvent.Type.RESOURCE_ADDED;
 import static org.onosproject.net.resource.ResourceEvent.Type.RESOURCE_REMOVED;
 
@@ -132,7 +133,7 @@
             // the order is preserved by LinkedHashMap
             Map<DiscreteResource, List<Resource>> resourceMap = resources.stream()
                     .filter(x -> x.parent().isPresent())
-                    .collect(Collectors.groupingBy(x -> x.parent().get(), LinkedHashMap::new, Collectors.toList()));
+                    .collect(groupingBy(x -> x.parent().get(), LinkedHashMap::new, Collectors.<Resource>toList()));
 
             TransactionalDiscreteResourceSubStore discreteTxStore = discreteStore.transactional(tx);
             TransactionalContinuousResourceSubStore continuousTxStore = continuousStore.transactional(tx);
diff --git a/core/store/dist/src/test/java/org/onosproject/store/impl/MastershipBasedTimestampTest.java b/core/store/dist/src/test/java/org/onosproject/store/impl/MastershipBasedTimestampTest.java
index 605de95..bbbc616 100644
--- a/core/store/dist/src/test/java/org/onosproject/store/impl/MastershipBasedTimestampTest.java
+++ b/core/store/dist/src/test/java/org/onosproject/store/impl/MastershipBasedTimestampTest.java
@@ -47,6 +47,7 @@
         assertEquals(sequenceNumber, ts.sequenceNumber());
     }
 
+    @SuppressWarnings("SelfComparison")
     @Test
     public final void testCompareTo() {
         assertTrue(TS_1_1.compareTo(TS_1_1) == 0);
diff --git a/core/store/primitives/pom.xml b/core/store/primitives/pom.xml
index a0b7473..5f4d799 100644
--- a/core/store/primitives/pom.xml
+++ b/core/store/primitives/pom.xml
@@ -70,7 +70,7 @@
         <dependency>
             <groupId>io.atomix</groupId>
             <artifactId>atomix</artifactId>
-            <version>2.0.12</version>
+            <version>2.0.14</version>
         </dependency>
 
         <dependency>
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartition.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartition.java
index c419d98..5d33e1c 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartition.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartition.java
@@ -265,7 +265,7 @@
         // Only need to do action if our membership changed
         if (wasPresent) {
             leaveCluster();
-        } else if (isPresent) {
+        } else {
             joinCluster();
         }
     }
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionClient.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionClient.java
index f5f8a99..723dc51 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionClient.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionClient.java
@@ -64,13 +64,15 @@
  */
 public class StoragePartitionClient implements DistributedPrimitiveCreator, Managed<StoragePartitionClient> {
 
+    private static final int MAX_RETRIES = 8;
+    private static final String ATOMIC_VALUES_CONSISTENT_MAP_NAME = "onos-atomic-values";
+
     private final Logger log = getLogger(getClass());
 
     private final StoragePartition partition;
     private final MemberId localMemberId;
     private final RaftClientProtocol protocol;
     private RaftClient client;
-    private static final String ATOMIC_VALUES_CONSISTENT_MAP_NAME = "onos-atomic-values";
     private final com.google.common.base.Supplier<AsyncConsistentMap<String, byte[]>> onosAtomicValuesMap =
             Suppliers.memoize(() -> newAsyncConsistentMap(ATOMIC_VALUES_CONSISTENT_MAP_NAME,
                                                           Serializer.using(KryoNamespaces.BASIC)));
@@ -110,7 +112,7 @@
                         .withReadConsistency(ReadConsistency.SEQUENTIAL)
                         .withCommunicationStrategy(CommunicationStrategy.ANY)
                         .withTimeout(Duration.ofSeconds(30))
-                        .withMaxRetries(5)
+                        .withMaxRetries(MAX_RETRIES)
                         .build()
                         .open()
                         .join());
@@ -135,7 +137,7 @@
                         .withReadConsistency(ReadConsistency.SEQUENTIAL)
                         .withCommunicationStrategy(CommunicationStrategy.ANY)
                         .withTimeout(Duration.ofSeconds(30))
-                        .withMaxRetries(5)
+                        .withMaxRetries(MAX_RETRIES)
                         .build()
                         .open()
                         .join());
@@ -159,7 +161,7 @@
                         .withReadConsistency(ReadConsistency.SEQUENTIAL)
                         .withCommunicationStrategy(CommunicationStrategy.ANY)
                         .withTimeout(Duration.ofSeconds(30))
-                        .withMaxRetries(5)
+                        .withMaxRetries(MAX_RETRIES)
                         .build()
                         .open()
                         .join());
@@ -189,7 +191,7 @@
                 .withReadConsistency(ReadConsistency.LINEARIZABLE_LEASE)
                 .withCommunicationStrategy(CommunicationStrategy.LEADER)
                 .withTimeout(Duration.ofSeconds(30))
-                .withMaxRetries(5)
+                .withMaxRetries(MAX_RETRIES)
                 .build()
                 .open()
                 .join());
@@ -211,7 +213,7 @@
                 .withReadConsistency(ReadConsistency.LINEARIZABLE_LEASE)
                 .withCommunicationStrategy(CommunicationStrategy.LEADER)
                 .withTimeout(Duration.ofSeconds(30))
-                .withMaxRetries(5)
+                .withMaxRetries(MAX_RETRIES)
                 .build()
                 .open()
                 .join());
@@ -235,7 +237,7 @@
                 .withReadConsistency(ReadConsistency.LINEARIZABLE_LEASE)
                 .withCommunicationStrategy(CommunicationStrategy.LEADER)
                 .withTimeout(Duration.ofSeconds(5))
-                .withMaxRetries(5)
+                .withMaxRetries(MAX_RETRIES)
                 .build()
                 .open()
                 .join());
@@ -250,7 +252,7 @@
                 .withReadConsistency(ReadConsistency.SEQUENTIAL)
                 .withCommunicationStrategy(CommunicationStrategy.ANY)
                 .withTimeout(Duration.ofSeconds(30))
-                .withMaxRetries(5)
+                .withMaxRetries(MAX_RETRIES)
                 .build()
                 .open()
                 .join());
@@ -266,7 +268,7 @@
                 .withCommunicationStrategy(CommunicationStrategy.LEADER)
                 .withMinTimeout(Duration.ofMillis(timeUnit.toMillis(leaderTimeout)))
                 .withMaxTimeout(Duration.ofSeconds(5))
-                .withMaxRetries(5)
+                .withMaxRetries(MAX_RETRIES)
                 .build()
                 .open()
                 .join());
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionServer.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionServer.java
index c9cfe53..1b201cd 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionServer.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionServer.java
@@ -49,7 +49,8 @@
 
     private static final int MAX_SEGMENT_SIZE = 1024 * 1024 * 64;
     private static final long ELECTION_TIMEOUT_MILLIS = 2500;
-    private static final long HEARTBEAT_INTERVAL_MILLIS = 250;
+    private static final int ELECTION_THRESHOLD = 5;
+    private static final long HEARTBEAT_INTERVAL_MILLIS = 500;
 
     private final MemberId localMemberId;
     private final StoragePartition partition;
@@ -143,9 +144,11 @@
                         clusterCommunicator))
                 .withElectionTimeout(Duration.ofMillis(ELECTION_TIMEOUT_MILLIS))
                 .withHeartbeatInterval(Duration.ofMillis(HEARTBEAT_INTERVAL_MILLIS))
+                .withElectionThreshold(ELECTION_THRESHOLD)
                 .withStorage(RaftStorage.newBuilder()
                         .withPrefix(String.format("partition-%s", partition.getId()))
-                        .withStorageLevel(StorageLevel.MAPPED)
+                        .withStorageLevel(StorageLevel.DISK)
+                        .withFlushOnCommit()
                         .withSerializer(new AtomixSerializerAdapter(Serializer.using(StorageNamespaces.RAFT_STORAGE)))
                         .withDirectory(partition.getDataFolder())
                         .withMaxSegmentSize(MAX_SEGMENT_SIZE)
@@ -200,9 +203,11 @@
                         clusterCommunicator))
                 .withElectionTimeout(Duration.ofMillis(ELECTION_TIMEOUT_MILLIS))
                 .withHeartbeatInterval(Duration.ofMillis(HEARTBEAT_INTERVAL_MILLIS))
+                .withElectionThreshold(ELECTION_THRESHOLD)
                 .withStorage(RaftStorage.newBuilder()
                         .withPrefix(String.format("partition-%s", partition.getId()))
-                        .withStorageLevel(StorageLevel.MAPPED)
+                        .withStorageLevel(StorageLevel.DISK)
+                        .withFlushOnCommit()
                         .withSerializer(new AtomixSerializerAdapter(Serializer.using(StorageNamespaces.RAFT_STORAGE)))
                         .withDirectory(partition.getDataFolder())
                         .withMaxSegmentSize(MAX_SEGMENT_SIZE)
diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/DefaultDocumentTree.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/DefaultDocumentTree.java
index 82098d3..2d5832c 100644
--- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/DefaultDocumentTree.java
+++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/DefaultDocumentTree.java
@@ -21,6 +21,7 @@
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.concurrent.atomic.AtomicLong;
 
 import org.onosproject.store.service.DocumentPath;
@@ -148,13 +149,17 @@
     }
 
     @Override
-    public boolean replace(DocumentPath path, V newValue, V currentValue) {
+    public boolean replace(DocumentPath path, V newValue, V expectedValue) {
         checkRootModification(path);
-        if (Objects.equals(newValue, currentValue)) {
+        if (Objects.equals(newValue, expectedValue)) {
             return false;
         }
         DocumentTreeNode<V> node = getNode(path);
-        if (node != null && Objects.equals(Versioned.valueOrNull(node.value()), currentValue)) {
+        V prevValue = Optional.ofNullable(node)
+                    .map(DocumentTreeNode::value)
+                    .map(Versioned::valueOrNull)
+                    .orElse(null);
+        if (Objects.equals(prevValue, expectedValue)) {
             set(path, newValue);
             return true;
         }
diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/impl/MapValueTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/impl/MapValueTest.java
index 70c33e1..cfad4e4 100644
--- a/core/store/primitives/src/test/java/org/onosproject/store/primitives/impl/MapValueTest.java
+++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/impl/MapValueTest.java
@@ -48,6 +48,7 @@
         assertEquals(actual, expected);
     }
 
+    @SuppressWarnings("SelfComparison")
     @Test
     public void testComparison() {
         Timestamp ts1 = new LogicalTimestamp(9);
diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/impl/TranscodingAsyncConsistentMapTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/impl/TranscodingAsyncConsistentMapTest.java
index 2cc9631..6466c12 100644
--- a/core/store/primitives/src/test/java/org/onosproject/store/primitives/impl/TranscodingAsyncConsistentMapTest.java
+++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/impl/TranscodingAsyncConsistentMapTest.java
@@ -115,6 +115,7 @@
         assertFalse(transcodingMap.containsValue(DEV1).join());
     }
 
+    @Test
     public void testGet() throws Exception {
         assertNull(transcodingMap.get(KEY1).join().value());
         transcodingMap.put(KEY2, DEV1).join();
diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapServiceTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapServiceTest.java
index c05ebcb..66173d0 100644
--- a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapServiceTest.java
+++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapServiceTest.java
@@ -15,8 +15,8 @@
  */
 package org.onosproject.store.primitives.resources.impl;
 
-import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 
 import io.atomix.protocols.raft.service.ServiceId;
 import io.atomix.protocols.raft.service.impl.DefaultCommit;
@@ -57,7 +57,7 @@
                 2,
                 PUT,
                 new AtomixConsistentSetMultimapOperations.Put(
-                        "foo", Arrays.asList("Hello world!".getBytes()), Match.ANY),
+                        "foo", Collections.singletonList("Hello world!".getBytes()), Match.ANY),
                 mock(RaftSessionContext.class),
                 System.currentTimeMillis()));
 
diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeTest.java
index 18746a7..7a41395 100644
--- a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeTest.java
+++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeTest.java
@@ -27,6 +27,7 @@
 import com.google.common.base.Throwables;
 import io.atomix.protocols.raft.proxy.RaftProxy;
 import io.atomix.protocols.raft.service.RaftService;
+
 import org.junit.Test;
 import org.onosproject.store.primitives.NodeUpdate;
 import org.onosproject.store.primitives.TransactionId;
@@ -219,6 +220,9 @@
         assertArrayEquals("newAB".getBytes(), tree.get(path("root.a.b")).join().value());
 
         assertFalse(tree.replace(path("root.a.d"), "bar".getBytes(), "foo".getBytes()).join());
+
+        assertTrue(tree.replace(path("root.x"), "beta".getBytes(), null).join());
+
     }
 
     /**
diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueServiceTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueServiceTest.java
index 919d3c9..2529523 100644
--- a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueServiceTest.java
+++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueServiceTest.java
@@ -15,8 +15,8 @@
  */
 package org.onosproject.store.primitives.resources.impl;
 
-import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 
 import io.atomix.protocols.raft.ReadConsistency;
 import io.atomix.protocols.raft.cluster.MemberId;
@@ -93,7 +93,7 @@
         service.add(new DefaultCommit<>(
                 2,
                 ADD,
-                new AtomixWorkQueueOperations.Add(Arrays.asList("Hello world!".getBytes())),
+                new AtomixWorkQueueOperations.Add(Collections.singletonList("Hello world!".getBytes())),
                 session,
                 System.currentTimeMillis()));
 
diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/DefaultDocumentTreeTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/DefaultDocumentTreeTest.java
index 5505e0d..1398300 100644
--- a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/DefaultDocumentTreeTest.java
+++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/DefaultDocumentTreeTest.java
@@ -148,6 +148,7 @@
         tree.set(path("root.a.b"), "alpha");
     }
 
+    @Test
     public void testReplaceWithVersion() {
         DocumentTree<String> tree = new DefaultDocumentTree<>();
         tree.create(path("root.a"), "bar");
@@ -158,6 +159,7 @@
         Assert.assertFalse(tree.replace(path("root.x"), "beta", 1));
     }
 
+    @Test
     public void testReplaceWithValue() {
         DocumentTree<String> tree = new DefaultDocumentTree<>();
         tree.create(path("root.a"), "bar");
diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/TestClusterCommunicationService.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/TestClusterCommunicationService.java
index a15bba4..791a488 100644
--- a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/TestClusterCommunicationService.java
+++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/TestClusterCommunicationService.java
@@ -72,9 +72,9 @@
 
     @Override
     public <M> void multicast(M message, MessageSubject subject, Function<M, byte[]> encoder, Set<NodeId> nodeIds) {
-        nodes.values().stream()
-                .filter(n -> nodeIds.contains(n))
-                .forEach(n -> n.handle(subject, encoder.apply(message)));
+        nodes.entrySet().stream()
+                .filter(e -> nodeIds.contains(e.getKey()))
+                .forEach(e -> e.getValue().handle(subject, encoder.apply(message)));
     }
 
     @Override
diff --git a/drivers/bmv2/BUCK b/drivers/bmv2/BUCK
index 63feb68..728f92b 100644
--- a/drivers/bmv2/BUCK
+++ b/drivers/bmv2/BUCK
@@ -28,6 +28,7 @@
     included_bundles = BUNDLES,
     required_apps = [
         'org.onosproject.drivers.p4runtime',
+        'org.onosproject.drivers.gnmi',
         'org.onosproject.pipelines.basic',
     ],
 )
diff --git a/drivers/bmv2/src/main/resources/bmv2-drivers.xml b/drivers/bmv2/src/main/resources/bmv2-drivers.xml
index 22bf067..0d6e0daa 100644
--- a/drivers/bmv2/src/main/resources/bmv2-drivers.xml
+++ b/drivers/bmv2/src/main/resources/bmv2-drivers.xml
@@ -15,7 +15,7 @@
   ~ limitations under the License.
   -->
 <drivers>
-    <driver name="bmv2" manufacturer="p4.org" hwVersion="master" swVersion="master" extends="p4runtime">
+    <driver name="bmv2" manufacturer="p4.org" hwVersion="master" swVersion="master" extends="p4runtime, gnmi">
         <behaviour api="org.onosproject.net.behaviour.PiPipelineProgrammable"
                    impl="org.onosproject.drivers.bmv2.Bmv2PipelineProgrammable"/>
     </driver>
diff --git a/drivers/ciena/src/main/java/org/onosproject/drivers/ciena/CienaWaveserverDeviceDescription.java b/drivers/ciena/src/main/java/org/onosproject/drivers/ciena/CienaWaveserverDeviceDescription.java
index 1c4114c..8a6113b 100644
--- a/drivers/ciena/src/main/java/org/onosproject/drivers/ciena/CienaWaveserverDeviceDescription.java
+++ b/drivers/ciena/src/main/java/org/onosproject/drivers/ciena/CienaWaveserverDeviceDescription.java
@@ -169,8 +169,9 @@
 
         //Working in Ghz //(Nominal central frequency - 193.1)/channelSpacing = spacingMultiplier
         final int baseFrequency = 193100;
+        long spacingFrequency = chSpacing == null ? baseFrequency : chSpacing.frequency().asHz();
         int spacingMult = ((int) (toGbps(((int) config.getDouble(frequency) -
-                baseFrequency)) / toGbpsFromHz(chSpacing.frequency().asHz()))); //FIXME is there a better way ?
+                baseFrequency)) / toGbpsFromHz(spacingFrequency))); //FIXME is there a better way ?
 
         return ochPortDescription(PortNumber.portNumber(portNumber), isEnabled, OduSignalType.ODU4, isTunable,
                                   new OchSignal(gridType, chSpacing, spacingMult, 1), annotations);
diff --git a/drivers/corsa/src/main/java/org/onosproject/drivers/corsa/CorsaPipelineV3.java b/drivers/corsa/src/main/java/org/onosproject/drivers/corsa/CorsaPipelineV3.java
index 098da40..3eb6261 100644
--- a/drivers/corsa/src/main/java/org/onosproject/drivers/corsa/CorsaPipelineV3.java
+++ b/drivers/corsa/src/main/java/org/onosproject/drivers/corsa/CorsaPipelineV3.java
@@ -82,10 +82,8 @@
                     switch (i.type()) {
                         case L2MODIFICATION:
                             L2ModificationInstruction l2i = (L2ModificationInstruction) i;
-                            if (l2i instanceof L2ModificationInstruction.ModVlanIdInstruction ||
-                                    l2i instanceof L2ModificationInstruction.ModEtherInstruction) {
-                                return true;
-                            }
+                            return l2i instanceof L2ModificationInstruction.ModVlanIdInstruction ||
+                                    l2i instanceof L2ModificationInstruction.ModEtherInstruction;
                         case OUTPUT:
                             return true;
                         default:
@@ -117,6 +115,7 @@
                             isPresentModEthDst = true;
                         }
                     }
+                    break;
                 case OUTPUT:
                     isPresentOutpuPort = true;
                 default:
diff --git a/drivers/corsa/src/main/java/org/onosproject/drivers/corsa/CorsaPipelineV39.java b/drivers/corsa/src/main/java/org/onosproject/drivers/corsa/CorsaPipelineV39.java
index bb2cd4d..9a684f5 100644
--- a/drivers/corsa/src/main/java/org/onosproject/drivers/corsa/CorsaPipelineV39.java
+++ b/drivers/corsa/src/main/java/org/onosproject/drivers/corsa/CorsaPipelineV39.java
@@ -234,12 +234,10 @@
                     switch (i.type()) {
                         case L2MODIFICATION:
                             L2ModificationInstruction l2i = (L2ModificationInstruction) i;
-                            if (l2i.subtype() == VLAN_ID ||
+                            return l2i.subtype() == VLAN_ID ||
                                     l2i.subtype() == VLAN_POP ||
                                     l2i.subtype() == ETH_DST ||
-                                    l2i.subtype() == ETH_SRC) {
-                                return true;
-                            }
+                                    l2i.subtype() == ETH_SRC;
                         case OUTPUT:
                             return true;
                         default:
@@ -270,6 +268,7 @@
                             isPresentModEthDst = true;
                         }
                     }
+                    break;
                 case OUTPUT:
                     isPresentOutpuPort = true;
                 default:
diff --git a/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraResubmitTable.java b/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraResubmitTable.java
index bb6347e..3e1dd05 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraResubmitTable.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/extensions/NiciraResubmitTable.java
@@ -65,7 +65,6 @@
      */
     public NiciraResubmitTable(PortNumber inPort, short table) {
         checkNotNull(inPort);
-        checkNotNull(table);
         this.inPort = inPort;
         this.table = table;
     }
@@ -76,7 +75,6 @@
      * @param table table
      */
     public NiciraResubmitTable(short table) {
-        checkNotNull(table);
         this.inPort = DEFAULT_IN_PORT;
         this.table = table;
     }
diff --git a/drivers/default/src/main/java/org/onosproject/driver/extensions/Ofdpa3SetMplsType.java b/drivers/default/src/main/java/org/onosproject/driver/extensions/Ofdpa3SetMplsType.java
index ca0f03d..a160b3a 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/extensions/Ofdpa3SetMplsType.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/extensions/Ofdpa3SetMplsType.java
@@ -49,7 +49,6 @@
      * @param mplsType MPLS type in short
      */
     public Ofdpa3SetMplsType(short mplsType) {
-        checkNotNull(mplsType);
         this.mplsType = mplsType;
     }
 
diff --git a/drivers/default/src/main/java/org/onosproject/driver/handshaker/Ofdpa3SwitchHandshaker.java b/drivers/default/src/main/java/org/onosproject/driver/handshaker/Ofdpa3SwitchHandshaker.java
new file mode 100644
index 0000000..40e54c1
--- /dev/null
+++ b/drivers/default/src/main/java/org/onosproject/driver/handshaker/Ofdpa3SwitchHandshaker.java
@@ -0,0 +1,52 @@
+/*
+ * 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.driver.handshaker;
+
+import org.projectfloodlight.openflow.protocol.OFCapabilities;
+import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Driver for ofdpa 3 switches
+ * TODO : Remove this and also remove the specific switch handler from
+ * onos-drivers.xml once bug with GROUP_STATS is fixed.
+ */
+public class Ofdpa3SwitchHandshaker extends DefaultSwitchHandshaker {
+
+    @Override
+    public void setFeaturesReply(OFFeaturesReply featuresReply) {
+
+        OFFeaturesReply.Builder builder = featuresReply.createBuilder();
+
+        // do not try to set PORTS or ACTIONS,
+        // they are not supported for this openflow version
+        builder.setAuxiliaryId(featuresReply.getAuxiliaryId());
+        builder.setDatapathId(featuresReply.getDatapathId());
+        builder.setNBuffers(featuresReply.getNBuffers());
+        builder.setReserved(featuresReply.getReserved());
+        builder.setXid(featuresReply.getXid());
+
+        Set<OFCapabilities> capabilities = new HashSet<>(featuresReply.getCapabilities());
+        capabilities.add(OFCapabilities.GROUP_STATS);
+        builder.setCapabilities(capabilities);
+
+        super.setFeaturesReply(builder.build());
+    }
+
+}
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/PicaPipeline.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/PicaPipeline.java
index 6c73693..ef79ee3 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/PicaPipeline.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/PicaPipeline.java
@@ -381,7 +381,8 @@
             return;
         }
 
-        EthCriterion e = null; VlanIdCriterion v = null;
+        EthCriterion e = null;
+        VlanIdCriterion v = null;
         Collection<IPCriterion> ips = new ArrayList<IPCriterion>();
         // convert filtering conditions for switch-intfs into flowrules
         FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
@@ -399,6 +400,12 @@
             }
         }
 
+        if (v == null || e == null) {
+            log.warn("Pica Pipeline ETH_DST and/or VLAN_ID not specified");
+            fail(filt, ObjectiveError.BADPARAMS);
+            return;
+        }
+
         // cache for later use
         Filter filter = new Filter(p, e, v, ips);
         filters.add(filter);
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/SoftRouterPipeline.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/SoftRouterPipeline.java
index 03a77ba..9ca9420 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/SoftRouterPipeline.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/SoftRouterPipeline.java
@@ -270,6 +270,12 @@
 
         }
 
+        if (v == null || e == null) {
+            log.warn("Soft Router Pipeline ETH_DST and/or VLAN_ID not specified");
+            fail(filt, ObjectiveError.BADPARAMS);
+            return;
+        }
+
         log.debug("Modifying Port/VLAN/MAC filtering rules in filter table: {}/{}/{}",
                   p.port(), v.vlanId(), e.mac());
         TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/CpqdOfdpa2Pipeline.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/CpqdOfdpa2Pipeline.java
index c5698a4..d8de983 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/CpqdOfdpa2Pipeline.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/CpqdOfdpa2Pipeline.java
@@ -336,14 +336,16 @@
         // ofdpa cannot match on ALL portnumber, so we need to use separate
         // rules for each port.
         List<PortNumber> portnums = new ArrayList<PortNumber>();
-        if (portCriterion.port() == PortNumber.ALL) {
-            for (Port port : deviceService.getPorts(deviceId)) {
-                if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
-                    portnums.add(port.number());
+        if (portCriterion != null) {
+            if (portCriterion.port() == PortNumber.ALL) {
+                for (Port port : deviceService.getPorts(deviceId)) {
+                    if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
+                        portnums.add(port.number());
+                    }
                 }
+            } else {
+                portnums.add(portCriterion.port());
             }
-        } else {
-            portnums.add(portCriterion.port());
         }
 
         List<FlowRule> rules = new ArrayList<FlowRule>();
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/CpqdOfdpa2VlanPipeline.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/CpqdOfdpa2VlanPipeline.java
index 701b76e..0279cb1 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/CpqdOfdpa2VlanPipeline.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/CpqdOfdpa2VlanPipeline.java
@@ -108,14 +108,16 @@
         // ofdpa cannot match on ALL portnumber, so we need to use separate
         // rules for each port.
         List<PortNumber> portnums = new ArrayList<PortNumber>();
-        if (portCriterion.port() == PortNumber.ALL) {
-            for (Port port : deviceService.getPorts(deviceId)) {
-                if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
-                    portnums.add(port.number());
+        if (portCriterion != null) {
+            if (portCriterion.port() == PortNumber.ALL) {
+                for (Port port : deviceService.getPorts(deviceId)) {
+                    if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
+                        portnums.add(port.number());
+                    }
                 }
+            } else {
+                portnums.add(portCriterion.port());
             }
-        } else {
-            portnums.add(portCriterion.port());
         }
 
         List<FlowRule> rules = new ArrayList<FlowRule>();
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa2GroupHandler.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa2GroupHandler.java
index bec569e..38cc72b 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa2GroupHandler.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa2GroupHandler.java
@@ -1440,6 +1440,7 @@
             // get a fresh copy of what the store holds
             NextGroup next = flowObjectiveStore.getNextGroup(nextObjective.id());
             List<Deque<GroupKey>> allActiveKeys = appKryo.deserialize(next.data());
+            allActiveKeys = Lists.newArrayList(allActiveKeys);
             // Note that since we got a new object, and ArrayDeque does not implement
             // Object.equals(), we have to check the deque elems one by one
             allActiveKeys
@@ -1814,6 +1815,7 @@
             }
             if (nextGrp.nextObjective().op() == Operation.ADD_TO_EXISTING) {
                 List<Deque<GroupKey>> allActiveKeys = appKryo.deserialize(next.data());
+                allActiveKeys = Lists.newArrayList(allActiveKeys);
                 // If active keys shows only the top-level group without a chain of groups,
                 // then it represents an empty group. Update by replacing empty chain.
                 if (allActiveKeys.size() == 1 && allActiveKeys.get(0).size() == 1) {
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa2Pipeline.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa2Pipeline.java
index a1cd29a..383ba4d 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa2Pipeline.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa2Pipeline.java
@@ -1044,7 +1044,7 @@
             for (Instruction ins : fwd.treatment().allInstructions()) {
                 if (ins instanceof OutputInstruction) {
                     OutputInstruction o = (OutputInstruction) ins;
-                    if (o != null && PortNumber.CONTROLLER.equals(o.port())) {
+                    if (PortNumber.CONTROLLER.equals(o.port())) {
                         ttBuilder.add(o);
                     } else {
                         log.warn("Only allowed treatments in versatile forwarding "
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/OfdpaGroupHandlerUtility.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/OfdpaGroupHandlerUtility.java
index f9cf3d0..df03656 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/OfdpaGroupHandlerUtility.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/OfdpaGroupHandlerUtility.java
@@ -534,22 +534,30 @@
 
         @Override
         public void run() {
-            if (groupHandler.pendingGroups().size() != 0) {
-                log.debug("pending groups being checked: {}", groupHandler.pendingGroups().asMap().keySet());
-            }
-            if (groupHandler.pendingAddNextObjectives().size() != 0) {
-                log.debug("pending add-next-obj being checked: {}",
-                          groupHandler.pendingAddNextObjectives().asMap().keySet());
-            }
-            Set<GroupKey> keys = groupHandler.pendingGroups().asMap().keySet().stream()
-                    .filter(key -> groupHandler.groupService.getGroup(groupHandler.deviceId, key) != null)
-                    .collect(Collectors.toSet());
-            Set<GroupKey> otherkeys = groupHandler.pendingAddNextObjectives().asMap().keySet().stream()
-                    .filter(otherkey -> groupHandler.groupService.getGroup(groupHandler.deviceId, otherkey) != null)
-                    .collect(Collectors.toSet());
-            keys.addAll(otherkeys);
+            // GroupChecker execution needs to be protected
+            // from unhandled exceptions
+            try {
+                if (groupHandler.pendingGroups().size() != 0) {
+                    log.debug("pending groups being checked: {}", groupHandler.pendingGroups().asMap().keySet());
+                }
+                if (groupHandler.pendingAddNextObjectives().size() != 0) {
+                    log.debug("pending add-next-obj being checked: {}",
+                              groupHandler.pendingAddNextObjectives().asMap().keySet());
+                }
+                Set<GroupKey> keys = groupHandler.pendingGroups().asMap().keySet().stream()
+                        .filter(key -> groupHandler.groupService.getGroup(groupHandler.deviceId, key) != null)
+                        .collect(Collectors.toSet());
+                Set<GroupKey> otherkeys = groupHandler.pendingAddNextObjectives().asMap().keySet().stream()
+                        .filter(otherkey -> groupHandler.groupService.getGroup(groupHandler.deviceId, otherkey) != null)
+                        .collect(Collectors.toSet());
+                keys.addAll(otherkeys);
 
-            keys.forEach(key -> groupHandler.processPendingAddGroupsOrNextObjs(key, false));
+                keys.forEach(key -> groupHandler.processPendingAddGroupsOrNextObjs(key, false));
+            } catch (Exception exception) {
+                // Just log. It is safe for now.
+                log.warn("Uncaught exception is detected: {}", exception.getMessage());
+                log.debug("Uncaught exception is detected (full stack trace): ", exception);
+            }
         }
     }
 }
diff --git a/drivers/default/src/main/resources/onos-drivers.xml b/drivers/default/src/main/resources/onos-drivers.xml
index dec58e5..180c852 100644
--- a/drivers/default/src/main/resources/onos-drivers.xml
+++ b/drivers/default/src/main/resources/onos-drivers.xml
@@ -76,6 +76,7 @@
 
     <!--  Driver for OFDPA 3.0 EA*.
        ~  TODO: version number from switch is still 2.0. Update when 3.0 is GA.
+       ~  TODO: remove Ofdpa3SwitchHandshaker when bug for GroupStats is fixed.
       -->
     <driver name="ofdpa3" extends="ofdpa"
             manufacturer="Broadcom Corp\." hwVersion="OF-DPA 2.0" swVersion="OF-DPA 2.0">
@@ -93,6 +94,8 @@
                    impl="org.onosproject.driver.extensions.Ofdpa3ExtensionTreatmentInterpreter" />
         <behaviour api="org.onosproject.net.flow.ExtensionSelectorCodec"
                    impl="org.onosproject.driver.extensions.Ofdpa3ExtensionSelectorInterpreter" />
+        <behaviour api="org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver"
+                   impl="org.onosproject.driver.handshaker.Ofdpa3SwitchHandshaker"/>
     </driver>
 
     <driver name="xpliant" extends="ofdpa3"
diff --git a/drivers/fujitsu/src/main/java/org/onosproject/drivers/fujitsu/FujitsuVoltControllerConfig.java b/drivers/fujitsu/src/main/java/org/onosproject/drivers/fujitsu/FujitsuVoltControllerConfig.java
index 9d5ba5c..b238b37 100644
--- a/drivers/fujitsu/src/main/java/org/onosproject/drivers/fujitsu/FujitsuVoltControllerConfig.java
+++ b/drivers/fujitsu/src/main/java/org/onosproject/drivers/fujitsu/FujitsuVoltControllerConfig.java
@@ -212,13 +212,13 @@
         try {
              editcfg = (XMLConfiguration) cfg;
         } catch (ClassCastException e) {
-            e.printStackTrace();
+            return null;
         }
         StringWriter stringWriter = new StringWriter();
         try {
             editcfg.save(stringWriter);
         } catch (ConfigurationException e) {
-            e.printStackTrace();
+            return null;
         }
         String s = stringWriter.toString();
         String fromStr = buildStartTag(TARGET, false) + target +
diff --git a/drivers/gnmi/BUCK b/drivers/gnmi/BUCK
new file mode 100644
index 0000000..0fe379e
--- /dev/null
+++ b/drivers/gnmi/BUCK
@@ -0,0 +1,38 @@
+PROTOBUF_VER = '3.2.0'
+GRPC_VER = '1.3.1'
+
+COMPILE_DEPS = [
+    '//lib:CORE_DEPS',
+    '//lib:KRYO',
+    '//protocols/grpc/api:onos-protocols-grpc-api',
+    '//protocols/grpc/proto:onos-protocols-grpc-proto',
+    '//incubator/grpc-dependencies:grpc-core-repkg-' + GRPC_VER,
+    '//lib:grpc-netty-' + GRPC_VER,
+    '//lib:protobuf-java-' + PROTOBUF_VER,
+    '//lib:grpc-stub-' + GRPC_VER,
+    '//core/store/serializers:onos-core-serializers',
+    '//protocols/gnmi/stub:onos-protocols-gnmi-stub',
+]
+
+BUNDLES = [
+    ':onos-drivers-gnmi',
+]
+
+osgi_jar (
+    deps = COMPILE_DEPS,
+)
+
+onos_app (
+    app_name = 'org.onosproject.drivers.gnmi',
+    title = 'gNMI Drivers',
+    category = 'Drivers',
+    url = 'http://onosproject.org',
+    description = 'Adds support for devices using gNMI protocol based on ' +
+    ' openconfig models: http://openconfig.net/ .',
+    included_bundles = BUNDLES,
+    required_apps = [
+        'org.onosproject.generaldeviceprovider',
+        'org.onosproject.protocols.grpc',
+        'org.onosproject.protocols.gnmi'
+    ],
+)
\ No newline at end of file
diff --git a/drivers/gnmi/features.xml b/drivers/gnmi/features.xml
new file mode 100644
index 0000000..448f07a
--- /dev/null
+++ b/drivers/gnmi/features.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+  ~ 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.
+  -->
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="${project.artifactId}-${project.version}">
+    <feature name="${project.artifactId}" version="${project.version}"
+             description="${project.description}">
+        <feature>onos-api</feature>
+        <bundle>mvn:${project.groupId}/${project.artifactId}/${project.version}</bundle>
+
+    </feature>
+</features>
diff --git a/drivers/gnmi/pom.xml b/drivers/gnmi/pom.xml
new file mode 100644
index 0000000..ae046d6
--- /dev/null
+++ b/drivers/gnmi/pom.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>onos-drivers-general</artifactId>
+        <groupId>org.onosproject</groupId>
+        <version>1.11.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>onos-drivers-gnmi</artifactId>
+    <packaging>bundle</packaging>
+
+    <description>gNMI device drivers</description>
+    <dependencies>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-grpc-protocol-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>io.grpc</groupId>
+            <artifactId>grpc-netty</artifactId>
+            <version>1.3.1</version>
+        </dependency>
+
+        <!-- protocols/gnmi/api missing -->
+
+    </dependencies>
+
+    <properties>
+        <onos.app.name>org.onosproject.drivers.gnmi</onos.app.name>
+        <onos.app.origin>ON.Lab</onos.app.origin>
+        <onos.app.title>gNMI Device Drivers</onos.app.title>
+        <onos.app.category>Drivers</onos.app.category>
+        <onos.app.url>http://onosproject.org</onos.app.url>
+        <onos.app.requires>
+            org.onosproject.generaldeviceprovider
+        </onos.app.requires>
+    </properties>
+
+</project>
\ No newline at end of file
diff --git a/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/GnmiDeviceDescriptionDiscovery.java b/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/GnmiDeviceDescriptionDiscovery.java
new file mode 100644
index 0000000..04c9f01
--- /dev/null
+++ b/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/GnmiDeviceDescriptionDiscovery.java
@@ -0,0 +1,305 @@
+/*
+ * 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.drivers.gnmi;
+
+import com.google.common.collect.ImmutableList;
+import gnmi.gNMIGrpc;
+import io.grpc.ManagedChannel;
+import io.grpc.ManagedChannelBuilder;
+import io.grpc.Status;
+import io.grpc.StatusRuntimeException;
+import io.grpc.internal.DnsNameResolverProvider;
+import io.grpc.netty.NettyChannelBuilder;
+import io.grpc.stub.StreamObserver;
+import org.onosproject.grpc.api.GrpcChannelId;
+import org.onosproject.grpc.api.GrpcController;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DefaultPortDescription;
+import org.onosproject.net.device.DeviceDescription;
+import org.onosproject.net.device.DeviceDescriptionDiscovery;
+import org.onosproject.net.device.PortDescription;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import static gnmi.Gnmi.Path;
+import static gnmi.Gnmi.PathElem;
+import static gnmi.Gnmi.SubscribeRequest;
+import static gnmi.Gnmi.SubscribeResponse;
+import static gnmi.Gnmi.Subscription;
+import static gnmi.Gnmi.SubscriptionList;
+import static gnmi.Gnmi.Update;
+
+/**
+ * Class that discovers the device description and ports of a device that
+ * supports the gNMI protocol and Openconfig models.
+ */
+public class GnmiDeviceDescriptionDiscovery
+        extends AbstractHandlerBehaviour
+        implements DeviceDescriptionDiscovery {
+
+    private static final int REQUEST_TIMEOUT_SECONDS = 5;
+
+    private static final Logger log = LoggerFactory
+            .getLogger(GnmiDeviceDescriptionDiscovery.class);
+
+    private static final String GNMI_SERVER_ADDR_KEY = "gnmi_ip";
+    private static final String GNMI_SERVER_PORT_KEY = "gnmi_port";
+
+    @Override
+    public DeviceDescription discoverDeviceDetails() {
+        return null;
+    }
+
+    @Override
+    public List<PortDescription> discoverPortDetails() {
+        log.info("Discovering port details on device {}", handler().data().deviceId());
+
+        // Get the channel
+        ManagedChannel channel = getChannel();
+
+        if (channel == null) {
+            return ImmutableList.of();
+        }
+
+        // Build the subscribe request
+        SubscribeRequest request = subscribeRequest();
+
+        // New stub
+        gNMIGrpc.gNMIStub gnmiStub = gNMIGrpc.newStub(channel);
+
+        final CompletableFuture<List<PortDescription>>
+                reply = new CompletableFuture<>();
+
+        // Subscribe to the replies
+        StreamObserver<SubscribeRequest> subscribeRequest = gnmiStub
+                .subscribe(new SubscribeResponseObserver(reply));
+        log.debug("Interfaces request {}", request);
+
+        List<PortDescription> ports;
+        try {
+            // Issue the request
+            subscribeRequest.onNext(request);
+            ports = reply.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        } catch (InterruptedException | ExecutionException | TimeoutException
+                | StatusRuntimeException e) {
+            log.warn("Unable to discover ports from {}: {}",
+                     data().deviceId(), e.getMessage());
+            log.debug("{}", e);
+            return ImmutableList.of();
+        } finally {
+            subscribeRequest.onCompleted();
+        }
+
+        return ports;
+    }
+
+    /**
+     * Obtains the ManagedChannel to be used for the communication.
+     *
+     * @return the managed channel
+     */
+    private ManagedChannel getChannel() {
+
+        DeviceId deviceId = handler().data().deviceId();
+        String serverAddr = this.data().value(GNMI_SERVER_ADDR_KEY);
+        String serverPortString = this.data().value(GNMI_SERVER_PORT_KEY);
+
+        GrpcController controller = handler().get(GrpcController.class);
+        ManagedChannel channel = null;
+
+        //FIXME can be optimized
+        //getting a channel if exists.
+        ManagedChannel managedChannel = controller
+                .getChannels(handler().data().deviceId()).stream().filter(c -> {
+                    String[] authority = c.authority().split(":");
+                    String host = authority[0];
+                    String port = authority[1];
+                    return host.equals(serverAddr) && port.equals(serverPortString);
+                }).findAny().orElse(null);
+
+        if (managedChannel != null) {
+            log.debug("Reusing Channel");
+            channel = managedChannel;
+        } else {
+            log.debug("Creating Channel");
+            GrpcChannelId newChannelId = GrpcChannelId.of(deviceId, "gnmi");
+
+            ManagedChannelBuilder channelBuilder = NettyChannelBuilder
+                    .forAddress(serverAddr, Integer.valueOf(serverPortString))
+                    .usePlaintext(true)
+                    .nameResolverFactory(new DnsNameResolverProvider());
+
+            try {
+                channel = controller.connectChannel(newChannelId, channelBuilder);
+            } catch (IOException e) {
+                log.warn("Unable to connect to gRPC server of {}: {}",
+                         deviceId, e.getMessage());
+            }
+        }
+        return channel;
+    }
+
+    /**
+     * Creates the subscribe request for the interfaces.
+     *
+     * @return subscribe request
+     */
+    private SubscribeRequest subscribeRequest() {
+        Path path = Path.newBuilder()
+                .addElem(PathElem.newBuilder().setName("interfaces").build())
+                .addElem(PathElem.newBuilder().setName("interface").build())
+                .addElem(PathElem.newBuilder().setName("...").build())
+                .build();
+        Subscription subscription = Subscription.newBuilder().setPath(path).build();
+        SubscriptionList list = SubscriptionList.newBuilder().setMode(SubscriptionList.Mode.ONCE)
+                .addSubscription(subscription).build();
+        return SubscribeRequest.newBuilder().setSubscribe(list).build();
+    }
+
+    /**
+     * Handles messages received from the device on the stream channel.
+     */
+    private final class SubscribeResponseObserver
+            implements StreamObserver<SubscribeResponse> {
+
+        private final CompletableFuture<List<PortDescription>> reply;
+
+        private SubscribeResponseObserver(CompletableFuture<List<PortDescription>> reply) {
+            this.reply = reply;
+        }
+
+        @Override
+        public void onNext(SubscribeResponse message) {
+            Map<String, DefaultPortDescription.Builder> ports = new HashMap<>();
+            Map<String, DefaultAnnotations.Builder> portsAnnotations = new HashMap<>();
+            log.debug("Response {} ", message.getUpdate().toString());
+            message.getUpdate().getUpdateList().forEach(update -> {
+                parseUpdate(ports, portsAnnotations, update);
+            });
+
+            List<PortDescription> portDescriptionList = new ArrayList<>();
+            ports.forEach((k, v) -> {
+//                v.portSpeed(1000L);
+                v.type(Port.Type.COPPER);
+                v.annotations(portsAnnotations.get(k).set("name", k).build());
+                portDescriptionList.add(v.build());
+            });
+
+            reply.complete(portDescriptionList);
+        }
+
+
+        @Override
+        public void onError(Throwable throwable) {
+            log.warn("Error on stream channel for {}: {}",
+                     data().deviceId(), Status.fromThrowable(throwable));
+            log.debug("{}", throwable);
+        }
+
+        @Override
+        public void onCompleted() {
+            log.debug("SubscribeResponseObserver completed");
+        }
+    }
+
+    /**
+     * Parses the update received from the device.
+     *
+     * @param ports            the ports description to build
+     * @param portsAnnotations the ports annotations list to populate
+     * @param update           the update received
+     */
+    private void parseUpdate(Map<String, DefaultPortDescription.Builder> ports,
+                             Map<String, DefaultAnnotations.Builder> portsAnnotations,
+                             Update update) {
+
+        //FIXME crude parsing, can be done via object (de)serialization
+        if (update.getPath().getElemList().size() > 3) {
+            String name = update.getPath().getElem(3).getName();
+            String portId = update.getPath().getElem(1).getKeyMap().get("name");
+            if (!ports.containsKey(portId)) {
+                int number = Character.getNumericValue(portId.charAt(portId.length() - 1));
+                PortNumber portNumber = PortNumber.portNumber(number, portId);
+                ports.put(portId, DefaultPortDescription.builder()
+                        .withPortNumer(portNumber));
+            }
+            if (name.equals("enabled")) {
+                DefaultPortDescription.Builder builder = ports.get(portId);
+                builder = builder.isEnabled(update.getVal().getBoolVal());
+                ports.put(portId, builder);
+            } else if (name.equals("state")) {
+                String speedName = update.getPath().getElem(4).getName();
+                if (speedName.equals("negotiated-port-speed")) {
+                    DefaultPortDescription.Builder builder = ports.get(portId);
+                    long speed = parsePortSpeed(update.getVal().getStringVal());
+                    builder = builder.portSpeed(speed);
+                    ports.put(portId, builder);
+                }
+            } else if (!name.equals("ifindex")) {
+                if (!portsAnnotations.containsKey(portId)) {
+                    portsAnnotations.put(portId, DefaultAnnotations.builder()
+                            .set(name, update.getVal().toByteString()
+                                    .toString(Charset.defaultCharset()).trim()));
+                } else {
+                    DefaultAnnotations.Builder builder = portsAnnotations.get(portId);
+                    builder = builder.set(name, update.getVal().toByteString().
+                            toString(Charset.defaultCharset()).trim());
+                    portsAnnotations.put(portId, builder);
+                }
+            }
+        }
+    }
+
+    private long parsePortSpeed(String speed) {
+        log.debug("Speed from config {}", speed);
+        switch (speed) {
+            case "SPEED_10MB":
+                return 10;
+            case "SPEED_100MB":
+                return 10;
+            case "SPEED_1GB":
+                return 1000;
+            case "SPEED_10GB":
+                return 10000;
+            case "SPEED_25GB":
+                return 25000;
+            case "SPEED_40GB":
+                return 40000;
+            case "SPEED_50GB":
+                return 50000;
+            case "SPEED_100GB":
+                return 100000;
+            default:
+                return 1000;
+        }
+    }
+}
diff --git a/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/GnmiDriversLoader.java b/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/GnmiDriversLoader.java
new file mode 100644
index 0000000..689a53b
--- /dev/null
+++ b/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/GnmiDriversLoader.java
@@ -0,0 +1,36 @@
+/*
+ * 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.drivers.gnmi;
+
+import org.apache.felix.scr.annotations.Component;
+import org.onosproject.net.driver.AbstractDriverLoader;
+
+/**
+ * Loader for gNMI device drivers.
+ */
+@Component(immediate = true)
+public class GnmiDriversLoader extends AbstractDriverLoader {
+
+    public GnmiDriversLoader() {
+        super("/gnmi-drivers.xml");
+    }
+
+    @Override
+    public void activate() {
+        super.activate();
+    }
+}
diff --git a/apps/restconf/utils/src/main/java/org/onosproject/restconf/utils/exceptions/package-info.java b/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/package-info.java
similarity index 87%
rename from apps/restconf/utils/src/main/java/org/onosproject/restconf/utils/exceptions/package-info.java
rename to drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/package-info.java
index a0fe88e..832ad5a 100644
--- a/apps/restconf/utils/src/main/java/org/onosproject/restconf/utils/exceptions/package-info.java
+++ b/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/package-info.java
@@ -15,6 +15,6 @@
  */
 
 /**
- * Parse utils custom exceptions.
+ * Package for gnmi device drivers.
  */
-package org.onosproject.restconf.utils.exceptions;
\ No newline at end of file
+package org.onosproject.drivers.gnmi;
\ No newline at end of file
diff --git a/drivers/gnmi/src/main/resources/gnmi-drivers.xml b/drivers/gnmi/src/main/resources/gnmi-drivers.xml
new file mode 100644
index 0000000..3744781
--- /dev/null
+++ b/drivers/gnmi/src/main/resources/gnmi-drivers.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<drivers>
+    <driver name="gnmi" manufacturer="gnmi" hwVersion="master" swVersion="master">
+        <behaviour api="org.onosproject.net.device.DeviceDescriptionDiscovery"
+                   impl="org.onosproject.drivers.gnmi.GnmiDeviceDescriptionDiscovery"/>
+    </driver>
+</drivers>
+
diff --git a/drivers/hp/src/main/java/org/onosproject/drivers/hp/HPPipelineV1.java b/drivers/hp/src/main/java/org/onosproject/drivers/hp/HPPipelineV1.java
index 48e6122..27a585b 100644
--- a/drivers/hp/src/main/java/org/onosproject/drivers/hp/HPPipelineV1.java
+++ b/drivers/hp/src/main/java/org/onosproject/drivers/hp/HPPipelineV1.java
@@ -261,7 +261,7 @@
                  */
                 if (instruction.type() == Instruction.Type.L2MODIFICATION) {
 
-                    if (!this.hardwareInstructionsL3mod.contains(((L2ModificationInstruction) instruction).subtype())) {
+                    if (!this.hardwareInstructionsL2mod.contains(((L2ModificationInstruction) instruction).subtype())) {
                         log.warn("HP V1 Driver - L2MODIFICATION.subtype {} only supported in SOFTWARE",
                                 ((L2ModificationInstruction) instruction).subtype());
 
diff --git a/drivers/hp/src/main/java/org/onosproject/drivers/hp/HPPipelineV3500.java b/drivers/hp/src/main/java/org/onosproject/drivers/hp/HPPipelineV3500.java
index a488439..06e01c1 100644
--- a/drivers/hp/src/main/java/org/onosproject/drivers/hp/HPPipelineV3500.java
+++ b/drivers/hp/src/main/java/org/onosproject/drivers/hp/HPPipelineV3500.java
@@ -252,7 +252,7 @@
                  * */
                 if (instruction.type() == Instruction.Type.L2MODIFICATION) {
 
-                    if (!this.hardwareInstructionsL3mod.contains(((L2ModificationInstruction) instruction).subtype())) {
+                    if (!this.hardwareInstructionsL2mod.contains(((L2ModificationInstruction) instruction).subtype())) {
                         log.warn("HP V3500 Driver - L2MODIFICATION.subtype {} only supported in SOFTWARE",
                                 ((L2ModificationInstruction) instruction).subtype());
 
diff --git a/drivers/microsemi/BUCK b/drivers/microsemi/BUCK
index 80ef2c8..210d76cc 100644
--- a/drivers/microsemi/BUCK
+++ b/drivers/microsemi/BUCK
@@ -15,7 +15,8 @@
     '//core/api:onos-api-tests',
     '//drivers/netconf:onos-drivers-netconf-tests',
     '//utils/osgi:onlab-osgi-tests',
-    '//incubator/net:onos-incubator-net'
+    '//incubator/net:onos-incubator-net',
+    '//incubator/net:onos-incubator-net-tests'
 ]
 
 APPS = [
diff --git a/drivers/microsemi/pom.xml b/drivers/microsemi/pom.xml
index ac299d4..4746d50 100644
--- a/drivers/microsemi/pom.xml
+++ b/drivers/microsemi/pom.xml
@@ -133,6 +133,21 @@
 
         <dependency>
             <groupId>org.onosproject</groupId>
+            <artifactId>onos-incubator-net</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-incubator-net</artifactId>
+            <version>${project.version}</version>
+            <classifier>tests</classifier>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
             <artifactId>onos-api</artifactId>
             <scope>test</scope>
             <classifier>tests</classifier>
@@ -140,25 +155,15 @@
 
         <dependency>
             <groupId>org.onosproject</groupId>
-            <artifactId>onos-incubator-net</artifactId>
+            <artifactId>onos-core-common</artifactId>
+            <classifier>tests</classifier>
             <scope>test</scope>
         </dependency>
 
+
     </dependencies>
 
     <build>
-        <pluginManagement>
-            <plugins>
-
-                <plugin>
-                    <groupId>org.apache.karaf.tooling</groupId>
-                    <artifactId>karaf-maven-plugin</artifactId>
-                    <version>3.0.5</version>
-                    <extensions>true</extensions>
-                </plugin>
-
-            </plugins>
-        </pluginManagement>
 
         <plugins>
             <plugin>
diff --git a/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/EA1000CfmMepProgrammable.java b/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/EA1000CfmMepProgrammable.java
index 143ee79..a5ff236 100755
--- a/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/EA1000CfmMepProgrammable.java
+++ b/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/EA1000CfmMepProgrammable.java
@@ -16,12 +16,17 @@
 package org.onosproject.drivers.microsemi;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.drivers.microsemi.yang.utils.MaNameUtil.getApiMaIdFromYangMaName;
+import static org.onosproject.drivers.microsemi.yang.utils.MdNameUtil.getApiMdIdFromYangMdName;
+import static org.onosproject.drivers.microsemi.yang.utils.MdNameUtil.getYangMdNameFromApiMdId;
 import static org.slf4j.LoggerFactory.getLogger;
 
 import java.time.Duration;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.NoSuchElementException;
+import java.util.Optional;
 
-import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 import org.onlab.util.HexString;
@@ -47,14 +52,12 @@
 import org.onosproject.incubator.net.l2monitoring.cfm.RemoteMepEntry.RemoteMepState;
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort;
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId;
-import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr;
-import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdDomainName;
-import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdMacUint;
-import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdNone;
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepKeyId;
 import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
 import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMdService;
 import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepProgrammable;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepService;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.driver.AbstractHandlerBehaviour;
@@ -62,7 +65,6 @@
 import org.onosproject.netconf.NetconfController;
 import org.onosproject.netconf.NetconfException;
 import org.onosproject.netconf.NetconfSession;
-import org.onosproject.yang.gen.v1.ietfinettypes.rev20130715.ietfinettypes.DomainName;
 import org.onosproject.yang.gen.v1.mseacfm.rev20160229.MseaCfm;
 import org.onosproject.yang.gen.v1.mseacfm.rev20160229.MseaCfmOpParam;
 import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.DefaultMefCfm;
@@ -83,11 +85,6 @@
 import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.maintenanceassociation.maintenanceassociationendpoint.ContinuityCheck;
 import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.maintenanceassociation.maintenanceassociationendpoint.DefaultContinuityCheck;
 import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.maintenanceassociation.maintenanceassociationendpoint.InterfaceEnum;
-import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.mdnameandtypecombo.DefaultMacAddressAndUint;
-import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.mdnameandtypecombo.DefaultNameCharacterString;
-import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.mdnameandtypecombo.DefaultNameDomainName;
-import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.mdnameandtypecombo.DefaultNameNone;
-import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.mdnameandtypecombo.namedomainname.NameDomainNameUnion;
 import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.remotemepstatetype.RemoteMepStateTypeEnum;
 import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.targetaddressgroup.addresstype.DefaultMacAddress;
 import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.targetaddressgroup.addresstype.DefaultMepId;
@@ -97,8 +94,6 @@
 import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.transmitloopback.transmitloopbackinput.TargetAddress;
 import org.onosproject.yang.gen.v1.mseasoamfm.rev20160229.mseasoamfm.mefcfm.maintenancedomain.maintenanceassociation.maintenanceassociationendpoint.AugmentedMseaCfmMaintenanceAssociationEndPoint;
 import org.onosproject.yang.gen.v1.mseasoamfm.rev20160229.mseasoamfm.mefcfm.maintenancedomain.maintenanceassociation.maintenanceassociationendpoint.DefaultAugmentedMseaCfmMaintenanceAssociationEndPoint;
-import org.onosproject.yang.gen.v1.mseatypes.rev20160229.mseatypes.Identifier45;
-import org.onosproject.yang.gen.v1.mseatypes.rev20160229.mseatypes.MacAddressAndUintStr;
 import org.onosproject.yang.gen.v1.mseatypes.rev20160229.mseatypes.MdLevelType;
 import org.onosproject.yang.gen.v1.mseatypes.rev20160229.mseatypes.MepIdType;
 import org.onosproject.yang.gen.v1.mseatypes.rev20160229.mseatypes.PriorityType;
@@ -128,46 +123,34 @@
     public boolean createMep(MdId mdName, MaIdShort maName, Mep mep)
             throws CfmConfigException {
         NetconfController controller = checkNotNull(handler().get(NetconfController.class));
-        NetconfSession session = controller.getDevicesMap()
-                        .get(handler().data().deviceId()).getSession();
+        NetconfSession session = checkNotNull(controller.getDevicesMap()
+                                .get(handler().data().deviceId()).getSession());
         MseaCfmNetconfService mseaCfmService =
                 checkNotNull(handler().get(MseaCfmNetconfService.class));
+        CfmMepService cfmMepService =
+                checkNotNull(handler().get(CfmMepService.class));
 
         MaintenanceAssociationEndPoint yangMep = buildYangMepFromApiMep(mep);
 
         CfmMdService cfmMdService = checkNotNull(handler().get(CfmMdService.class));
-        MaintenanceDomain md = cfmMdService.getMaintenanceDomain(mdName).get();
-        MaintenanceAssociation ma = cfmMdService.getMaintenanceAssociation(mdName, maName).get();
+        MseaCfmOpParam mseaCfmOpParam = getMaYangObject(cfmMdService, mdName, maName);
 
-        if (!ma.remoteMepIdList().contains(mep.mepId())) {
-            throw new CfmConfigException("Mep Id " + mep.mepId() +
-                    " is not present in the remote Mep list for MA " + ma.maId() +
-                    ". This is required for EA1000.");
-        } else if (md.mdNumericId() <= 0 || md.mdNumericId() > NUMERIC_ID_MAX) {
-            throw new CfmConfigException("Numeric id of MD " + mdName + " must"
-                    + " be between 1 and 64 inclusive for EA1000");
-        } else if (ma.maNumericId() <= 0 || ma.maNumericId() > NUMERIC_ID_MAX) {
-            throw new CfmConfigException("Numeric id of MA " + maName + " must"
-                    + " be between 1 and 64 inclusive for EA1000");
-        }
+        mseaCfmOpParam.mefCfm().maintenanceDomain().get(0)
+                .maintenanceAssociation().get(0).addToMaintenanceAssociationEndPoint(yangMep);
+        //Add this mepId to the list of remoteMeps on the device
+        mseaCfmOpParam.mefCfm().maintenanceDomain().get(0)
+                .maintenanceAssociation().get(0).addToRemoteMeps(MepIdType.of(mep.mepId().value()));
 
-        org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain
-        .MaintenanceAssociation yangMa = buildYangMaFromApiMa(ma);
-        yangMa.addToMaintenanceAssociationEndPoint(yangMep);
-
-        org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm
-        .mefcfm.MaintenanceDomain yangMd = buildYangMdFromApiMd(md);
-        yangMd.addToMaintenanceAssociation(yangMa);
-
-        MefCfm mefCfm = new DefaultMefCfm();
-        mefCfm.addToMaintenanceDomain(yangMd);
-
-        MseaCfmOpParam mseaCfmOpParam = new MseaCfmOpParam();
-        mseaCfmOpParam.mefCfm(mefCfm);
+        //Add all of the existing meps on this MD/MA to the remote meps list
+        cfmMepService.getAllMeps(mdName, maName).forEach(m -> {
+            mseaCfmOpParam.mefCfm().maintenanceDomain().get(0)
+                    .maintenanceAssociation().get(0).addToRemoteMeps(MepIdType.of(m.mepId().value()));
+        });
         try {
             mseaCfmService.setMseaCfm(mseaCfmOpParam, session, DatastoreId.RUNNING);
             log.info("Created MEP {} on device {}", mdName + "/" + maName +
                     "/" + mep.mepId(), handler().data().deviceId());
+
             return true;
         } catch (NetconfException e) {
             log.error("Unable to create MEP {}/{}/{} on device {}",
@@ -177,11 +160,6 @@
     }
 
     @Override
-    public Collection<MepEntry> getAllMeps(MdId mdName, MaIdShort maName) throws CfmConfigException {
-        throw new UnsupportedOperationException("Not yet implemented");
-    }
-
-    @Override
     public MepEntry getMep(MdId mdName, MaIdShort maName, MepId mepId)
             throws CfmConfigException {
         NetconfController controller = checkNotNull(handler().get(NetconfController.class));
@@ -189,31 +167,20 @@
             throw new CfmConfigException("Device is not ready - connecting or "
                     + "disconnected for MEP " + mdName + "/" + maName + "/" + mepId);
         }
-        NetconfSession session = controller.getDevicesMap().get(handler().data().deviceId()).getSession();
+        NetconfSession session = checkNotNull(controller.getDevicesMap()
+                                .get(handler().data().deviceId()).getSession());
         MseaCfmNetconfService mseaCfmService = checkNotNull(handler().get(MseaCfmNetconfService.class));
 
         try {
             MseaCfm mseacfm =
                     mseaCfmService.getMepFull(mdName, maName, mepId, session);
-            if (mseacfm != null && mseacfm.mefCfm() != null &&
-                    mseacfm.mefCfm().maintenanceDomain() != null) {
-                for (org.onosproject.yang.gen.v1.mseacfm.rev20160229.
-                        mseacfm.mefcfm.MaintenanceDomain replyMd :
-                                        mseacfm.mefCfm().maintenanceDomain()) {
-                    for (org.onosproject.yang.gen.v1.mseacfm.rev20160229.
-                            mseacfm.mefcfm.maintenancedomain.
-                            MaintenanceAssociation replyMa :
-                                            replyMd.maintenanceAssociation()) {
-                        for (MaintenanceAssociationEndPoint replyMep :
-                                    replyMa.maintenanceAssociationEndPoint()) {
-                            return buildApiMepEntryFromYangMep(
-                                    replyMep, handler().data().deviceId(), mdName, maName);
-                        }
-                    }
-                }
+            Collection<MepEntry> mepEntries = getMepEntriesFromYangResponse(mseacfm);
+            if (mepEntries == null || mepEntries.size() != 1) {
+                log.warn("Mep " + mepId + " not found on device " + handler().data().deviceId());
+                return null;
+            } else {
+                return mepEntries.stream().findFirst().get();
             }
-            log.warn("Mep " + mepId + " not found on device " + handler().data().deviceId());
-            return null;
         } catch (NetconfException e) {
             log.error("Unable to get MEP {}/{}/{} on device {}",
                     mdName, maName, mepId, handler().data().deviceId());
@@ -221,12 +188,37 @@
         }
     }
 
+    private Collection<MepEntry> getMepEntriesFromYangResponse(MseaCfm mseacfm)
+            throws CfmConfigException {
+
+        Collection<MepEntry> mepEntries = new ArrayList<>();
+        if (mseacfm == null || mseacfm.mefCfm() == null || mseacfm.mefCfm().maintenanceDomain() == null) {
+            return mepEntries;
+        }
+
+        for (org.onosproject.yang.gen.v1.mseacfm.rev20160229.
+                mseacfm.mefcfm.MaintenanceDomain replyMd:mseacfm.mefCfm().maintenanceDomain()) {
+            for (org.onosproject.yang.gen.v1.mseacfm.rev20160229.
+                    mseacfm.mefcfm.maintenancedomain.
+                    MaintenanceAssociation replyMa:replyMd.maintenanceAssociation()) {
+                for (MaintenanceAssociationEndPoint replyMep:replyMa.maintenanceAssociationEndPoint()) {
+                    mepEntries.add(buildApiMepEntryFromYangMep(
+                        replyMep, handler().data().deviceId(), replyMd, replyMa));
+                }
+            }
+        }
+        return mepEntries;
+    }
+
     @Override
-    public boolean deleteMep(MdId mdName, MaIdShort maName, MepId mepId) throws CfmConfigException {
+    public boolean deleteMep(MdId mdName, MaIdShort maName, MepId mepId,
+                    Optional<MaintenanceDomain> oldMd) throws CfmConfigException {
 
         NetconfController controller = checkNotNull(handler().get(NetconfController.class));
-        NetconfSession session = controller.getDevicesMap().get(handler().data().deviceId()).getSession();
+        NetconfSession session = checkNotNull(controller.getDevicesMap()
+                .get(handler().data().deviceId()).getSession());
         MseaCfmNetconfService mseaCfmService = checkNotNull(handler().get(MseaCfmNetconfService.class));
+        CfmMdService mdService = checkNotNull(handler().get(CfmMdService.class));
 
         MaintenanceAssociationEndPoint mep =
                 new DefaultMaintenanceAssociationEndPoint();
@@ -234,12 +226,34 @@
 
         org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain
             .MaintenanceAssociation yangMa = new DefaultMaintenanceAssociation();
-        yangMa.maNameAndTypeCombo(MaNameUtil.getYangMaNameFromApiMaId(maName));
+        Short maNumericId = null;
+        try {
+            maNumericId =
+                    mdService.getMaintenanceAssociation(mdName, maName).get().maNumericId();
+            yangMa.id(maNumericId);
+        } catch (NoSuchElementException | IllegalArgumentException e) {
+            //The MA and/or MD have probably been deleted
+            // try to get numeric id values from oldMd
+            log.debug("Could not get MD/MA details from MD service during deletion of MEP {}." +
+                    "Continuing with values from event", new MepKeyId(mdName, maName, mepId));
+            yangMa.id(getMaNumericId(oldMd.get(), maName));
+        }
+
         yangMa.addToMaintenanceAssociationEndPoint(mep);
 
         org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.MaintenanceDomain yangMd =
             new DefaultMaintenanceDomain();
-        yangMd.mdNameAndTypeCombo(getYangMdNameFromApiMdId(mdName));
+        Short mdNumericId = null;
+        try {
+            mdNumericId = mdService.getMaintenanceDomain(mdName).get().mdNumericId();
+            yangMd.id(mdNumericId);
+        } catch (NoSuchElementException | IllegalArgumentException e) {
+            //The MD has probably been deleted
+            // try to get numeric id values from oldMd
+            log.debug("Could not get MD details from MD service during deletion of MEP {}." +
+                    "Continuing with values from event", new MepKeyId(mdName, maName, mepId));
+            yangMd.id(oldMd.get().mdNumericId());
+        }
         yangMd.addToMaintenanceAssociation(yangMa);
 
         MefCfm mefCfm = new DefaultMefCfm();
@@ -254,14 +268,281 @@
                     "/" + mepId, handler().data().deviceId());
             return true;
         } catch (NetconfException e) {
-            log.error("Unable to delete MEP {}/{}/{} on device {}",
-                    mdName, maName, mepId, handler().data().deviceId());
+            log.error("Unable to delete MEP {} ({}) on device {}",
+                    mdName + "/" + maName + "/" + mepId,
+                    mdNumericId + "/" + maNumericId, handler().data().deviceId(), e);
             throw new CfmConfigException("Unable to delete MEP :" + e.getMessage());
         }
 
     }
 
     @Override
+    public boolean createMdOnDevice(MdId mdId) throws CfmConfigException {
+        NetconfController controller =
+                checkNotNull(handler().get(NetconfController.class));
+        NetconfSession session = checkNotNull(controller.getDevicesMap()
+                .get(handler().data().deviceId()).getSession());
+
+        CfmMdService cfmMdService = checkNotNull(handler().get(CfmMdService.class));
+        MaintenanceDomain md = cfmMdService.getMaintenanceDomain(mdId).get();
+
+        org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm
+                .mefcfm.MaintenanceDomain yangMd = buildYangMdFromApiMd(md);
+
+        if (md.mdNumericId() <= 0 || md.mdNumericId() > NUMERIC_ID_MAX) {
+            throw new CfmConfigException("Numeric id of MD " + mdId + " must"
+                    + " be between 1 and 64 inclusive for EA1000");
+        }
+
+        for (MaintenanceAssociation ma:md.maintenanceAssociationList()) {
+            if (ma.maNumericId() <= 0 || ma.maNumericId() > NUMERIC_ID_MAX) {
+                throw new CfmConfigException("Numeric id of MA " + mdId + " must"
+                        + " be between 1 and 64 inclusive for EA1000");
+            }
+            org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain
+                    .MaintenanceAssociation yangMa = buildYangMaFromApiMa(ma);
+            yangMd.addToMaintenanceAssociation(yangMa);
+        }
+
+        MefCfm mefCfm = new DefaultMefCfm();
+        mefCfm.addToMaintenanceDomain(yangMd);
+
+        MseaCfmOpParam mseaCfmOpParam = new MseaCfmOpParam();
+        mseaCfmOpParam.mefCfm(mefCfm);
+
+        MseaCfmNetconfService mseaCfmService =
+                checkNotNull(handler().get(MseaCfmNetconfService.class));
+
+        try {
+            boolean created = mseaCfmService.setMseaCfm(mseaCfmOpParam, session, DatastoreId.RUNNING);
+            log.info("Created MD {} on device {}", mdId.mdName(),
+                    handler().data().deviceId());
+            return created;
+        } catch (NetconfException e) {
+            log.error("Unable to create MD {} on device {}",
+                    mdId.mdName(), handler().data().deviceId());
+            throw new CfmConfigException("Unable to create MD :" + e.getMessage());
+        }
+    }
+
+    @Override
+    public boolean createMaOnDevice(MdId mdId, MaIdShort maId) throws CfmConfigException {
+        NetconfController controller =
+                checkNotNull(handler().get(NetconfController.class));
+        NetconfSession session = checkNotNull(controller.getDevicesMap()
+                .get(handler().data().deviceId()).getSession());
+
+        CfmMdService mdService = checkNotNull(handler().get(CfmMdService.class));
+        MseaCfmOpParam mseaCfmOpParam = getMaYangObject(mdService, mdId, maId);
+        MseaCfmNetconfService mseaCfmService =
+                checkNotNull(handler().get(MseaCfmNetconfService.class));
+
+        try {
+            boolean created = mseaCfmService.setMseaCfm(mseaCfmOpParam, session, DatastoreId.RUNNING);
+            log.info("Created MA {} on device {}", mdId.mdName() + "/" + maId.maName(),
+                    handler().data().deviceId());
+            return created;
+        } catch (NetconfException e) {
+            log.error("Unable to create MA {} on device {}",
+                    mdId.mdName() + "/" + maId.maName(), handler().data().deviceId());
+            throw new CfmConfigException("Unable to create MA :" + e.getMessage());
+        }
+    }
+
+    private static MseaCfmOpParam getMaYangObject(CfmMdService cfmMdService,
+                        MdId mdName, MaIdShort maName) throws CfmConfigException {
+        MaintenanceDomain md = cfmMdService.getMaintenanceDomain(mdName).get();
+        MaintenanceAssociation ma = cfmMdService.getMaintenanceAssociation(mdName, maName).get();
+
+        org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain
+                .MaintenanceAssociation yangMa = buildYangMaFromApiMa(ma);
+
+        org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm
+                .mefcfm.MaintenanceDomain yangMd = buildYangMdFromApiMd(md);
+        yangMd.addToMaintenanceAssociation(yangMa);
+
+        if (md.mdNumericId() <= 0 || md.mdNumericId() > NUMERIC_ID_MAX) {
+            throw new CfmConfigException("Numeric id of MD " + mdName + " must"
+                    + " be between 1 and 64 inclusive for EA1000");
+        } else if (ma.maNumericId() <= 0 || ma.maNumericId() > NUMERIC_ID_MAX) {
+            throw new CfmConfigException("Numeric id of MA " + maName + " must"
+                    + " be between 1 and 64 inclusive for EA1000");
+        }
+
+        MefCfm mefCfm = new DefaultMefCfm();
+        mefCfm.addToMaintenanceDomain(yangMd);
+
+        MseaCfmOpParam mseaCfmOpParam = new MseaCfmOpParam();
+        mseaCfmOpParam.mefCfm(mefCfm);
+
+        return mseaCfmOpParam;
+    }
+
+    @Override
+    public boolean deleteMdOnDevice(MdId mdId, Optional<MaintenanceDomain> oldMd)
+            throws CfmConfigException {
+        NetconfController controller =
+                checkNotNull(handler().get(NetconfController.class));
+        NetconfSession session = controller.getDevicesMap()
+                .get(handler().data().deviceId()).getSession();
+
+        //First check if this MD is known to ONOS if it is does it have MAs and
+        // do they have any Meps known to ONOS. If there are Meps throw an exception -
+        // the Meps should have been deleted first
+        //If there are none known to ONOS we do not check for Meps on the actual device
+        // - there might might be some orphaned ones down there - we want to delete these
+        //FIXME: When CfmMepService is extended to be persistent come back and enable check
+        CfmMdService mdService = checkNotNull(handler().get(CfmMdService.class));
+        MseaCfmNetconfService mseaCfmService =
+                checkNotNull(handler().get(MseaCfmNetconfService.class));
+
+        MdNameAndTypeCombo mdName = getYangMdNameFromApiMdId(mdId);
+        org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.MaintenanceDomain yangMd =
+                new DefaultMaintenanceDomain();
+        Short mdNumericId = null;
+        try {
+            mdNumericId = mdService.getMaintenanceDomain(mdId).get().mdNumericId();
+            yangMd.id(mdNumericId);
+        } catch (NoSuchElementException e) {
+            yangMd.id(oldMd.get().mdNumericId());
+        }
+
+        MefCfm mefCfm = new DefaultMefCfm();
+        mefCfm.addToMaintenanceDomain(yangMd);
+
+        MseaCfmOpParam mseaCfmOpParam = new MseaCfmOpParam();
+        mseaCfmOpParam.mefCfm(mefCfm);
+
+        try {
+            boolean deleted = mseaCfmService.deleteMseaMd(mseaCfmOpParam, session, DatastoreId.RUNNING);
+            log.info("Deleted MD {} on device {}", mdName,
+                    handler().data().deviceId());
+            return deleted;
+        } catch (NetconfException e) {
+            log.error("Unable to delete MD {} ({}) on device {}",
+                    mdName, mdNumericId, handler().data().deviceId());
+            throw new CfmConfigException("Unable to delete MD :" + e.getMessage());
+        }
+
+    }
+
+    @Override
+    public boolean deleteMaOnDevice(MdId mdId, MaIdShort maId, Optional<MaintenanceDomain> oldMd)
+            throws CfmConfigException {
+        NetconfController controller =
+                checkNotNull(handler().get(NetconfController.class));
+        NetconfSession session = controller.getDevicesMap()
+                .get(handler().data().deviceId()).getSession();
+
+        CfmMdService mdService = checkNotNull(handler().get(CfmMdService.class));
+
+        MseaCfmNetconfService mseaCfmService =
+                checkNotNull(handler().get(MseaCfmNetconfService.class));
+
+        org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain
+                .MaintenanceAssociation yangMa = new DefaultMaintenanceAssociation();
+        Short maNumericId = null;
+        try {
+            maNumericId =
+                    mdService.getMaintenanceAssociation(mdId, maId).get().maNumericId();
+            yangMa.id(maNumericId);
+        } catch (NoSuchElementException e) {
+            yangMa.id(getMaNumericId(oldMd.get(), maId));
+        }
+
+        org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.MaintenanceDomain yangMd =
+                new DefaultMaintenanceDomain();
+        Short mdNumericId = null;
+        try {
+            mdNumericId = mdService.getMaintenanceDomain(mdId).get().mdNumericId();
+            yangMd.id(mdNumericId);
+        } catch (NoSuchElementException e) {
+            yangMd.id(oldMd.get().mdNumericId());
+        }
+        yangMd.addToMaintenanceAssociation(yangMa);
+
+        MefCfm mefCfm = new DefaultMefCfm();
+        mefCfm.addToMaintenanceDomain(yangMd);
+
+        MseaCfmOpParam mseaCfmOpParam = new MseaCfmOpParam();
+        mseaCfmOpParam.mefCfm(mefCfm);
+
+        try {
+            boolean deleted = mseaCfmService.deleteMseaMa(mseaCfmOpParam, session, DatastoreId.RUNNING);
+            log.info("Deleted MA {} ({})on device {}", mdId.mdName() + "/" + maId.maName(),
+                    mdNumericId + "/" + maNumericId, handler().data().deviceId());
+            return deleted;
+        } catch (NetconfException e) {
+            log.error("Unable to delete MA {} ({}) on device {}",
+                    mdId.mdName() + "/" + maId.maName(),
+                    mdNumericId + "/" + maNumericId, handler().data().deviceId());
+            throw new CfmConfigException("Unable to delete MA :" + e.getMessage());
+        }
+    }
+
+    @Override
+    public boolean createMaRemoteMepOnDevice(MdId mdId, MaIdShort maId, MepId remoteMep) throws CfmConfigException {
+        return crDelMaRemoteMep(mdId, maId, remoteMep, true);
+    }
+
+    @Override
+    public boolean deleteMaRemoteMepOnDevice(MdId mdId, MaIdShort maId, MepId remoteMep) throws CfmConfigException {
+        return crDelMaRemoteMep(mdId, maId, remoteMep, false);
+    }
+
+    private boolean crDelMaRemoteMep(MdId mdId, MaIdShort maId, MepId remoteMep,
+                                     boolean isCreate) throws CfmConfigException {
+        NetconfController controller =
+                checkNotNull(handler().get(NetconfController.class));
+        NetconfSession session = controller.getDevicesMap()
+                .get(handler().data().deviceId()).getSession();
+
+        CfmMdService mdService = checkNotNull(handler().get(CfmMdService.class));
+
+        Short mdNumericId = mdService.getMaintenanceDomain(mdId).get().mdNumericId();
+        Short maNumericId =
+                mdService.getMaintenanceAssociation(mdId, maId).get().maNumericId();
+
+        MseaCfmNetconfService mseaCfmService =
+                checkNotNull(handler().get(MseaCfmNetconfService.class));
+
+        org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain
+                .MaintenanceAssociation yangMa = new DefaultMaintenanceAssociation();
+        yangMa.id(maNumericId);
+        yangMa.addToRemoteMeps(MepIdType.of(remoteMep.value()));
+
+        org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.MaintenanceDomain yangMd =
+                new DefaultMaintenanceDomain();
+        yangMd.id(mdNumericId);
+        yangMd.addToMaintenanceAssociation(yangMa);
+
+        MefCfm mefCfm = new DefaultMefCfm();
+        mefCfm.addToMaintenanceDomain(yangMd);
+
+        MseaCfmOpParam mseaCfmOpParam = new MseaCfmOpParam();
+        mseaCfmOpParam.mefCfm(mefCfm);
+
+        try {
+            boolean result = false;
+            if (isCreate) {
+                result = mseaCfmService.setMseaCfm(mseaCfmOpParam, session, DatastoreId.RUNNING);
+            } else {
+                result = mseaCfmService.deleteMseaMaRMep(mseaCfmOpParam, session, DatastoreId.RUNNING);
+            }
+            log.info("{} Remote MEP {} in MA {} on device {}", isCreate ? "Created" : "Deleted",
+                    remoteMep, mdId.mdName() + "/" + maId.maName(), handler().data().deviceId());
+            return result;
+        } catch (NetconfException e) {
+            log.error("Unable to {} RemoteMep {} in MA {} on device {}",
+                    isCreate ? "create" : "delete", remoteMep, mdId.mdName() + "/" + maId.maName(),
+                    handler().data().deviceId());
+            throw new CfmConfigException("Unable to " + (isCreate ? "create" : "delete")
+                    + " Remote Mep:" + e.getMessage());
+        }
+    }
+
+
+    @Override
     public void transmitLoopback(MdId mdName, MaIdShort maName, MepId mepId,
             MepLbCreate lbCreate) throws CfmConfigException {
         NetconfController controller =
@@ -361,7 +642,7 @@
         throw new UnsupportedOperationException("Not yet implemented");
     }
 
-    private org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm
+    private static org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm
             .MaintenanceDomain buildYangMdFromApiMd(MaintenanceDomain md)
             throws CfmConfigException {
         MdNameAndTypeCombo mdName = getYangMdNameFromApiMdId(md.mdId());
@@ -375,44 +656,7 @@
         return mdYang;
     }
 
-    protected static MdNameAndTypeCombo getYangMdNameFromApiMdId(MdId mdId)
-            throws CfmConfigException {
-        MdNameAndTypeCombo mdName;
-        if (mdId instanceof MdIdDomainName) {
-            boolean isIpAddr = false;
-            try {
-                if (IpAddress.valueOf(mdId.mdName()) != null) {
-                    isIpAddr = true;
-                }
-            } catch (IllegalArgumentException e) {
-                //continue
-            }
-            if (isIpAddr) {
-                mdName = new DefaultNameDomainName();
-                ((DefaultNameDomainName) mdName).nameDomainName(NameDomainNameUnion.of(
-                                org.onosproject.yang.gen.v1.ietfinettypes.rev20130715.ietfinettypes.
-                                IpAddress.fromString(mdId.mdName())));
-            } else {
-                mdName = new DefaultNameDomainName();
-                ((DefaultNameDomainName) mdName).nameDomainName(NameDomainNameUnion
-                                    .of(DomainName.fromString(mdId.mdName())));
-            }
-        } else if (mdId instanceof MdIdMacUint) {
-            mdName = new DefaultMacAddressAndUint();
-            ((DefaultMacAddressAndUint) mdName).nameMacAddressAndUint(MacAddressAndUintStr.fromString(mdId.mdName()));
-        } else if (mdId instanceof MdIdNone) {
-            mdName = new DefaultNameNone();
-        } else if (mdId instanceof MdIdCharStr) {
-            mdName = new DefaultNameCharacterString();
-            ((DefaultNameCharacterString) mdName).name(Identifier45.fromString(mdId.mdName()));
-        } else {
-            throw new CfmConfigException("Unexpected error creating MD " +
-                    mdId.getClass().getSimpleName());
-        }
-        return mdName;
-    }
-
-    private org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm
+    private static org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm
             .maintenancedomain.MaintenanceAssociation buildYangMaFromApiMa(
                         MaintenanceAssociation apiMa) throws CfmConfigException {
 
@@ -422,12 +666,6 @@
                 .MaintenanceAssociation yamgMa = new DefaultMaintenanceAssociation();
         yamgMa.maNameAndTypeCombo(maName);
 
-        if (apiMa.remoteMepIdList() == null || apiMa.remoteMepIdList().size() < REMOTEMEPLIST_MIN_COUNT
-                || apiMa.remoteMepIdList().size() > REMOTEMEPLIST_MAX_COUNT) {
-            throw new CfmConfigException("EA1000 requires between " +
-                    REMOTEMEPLIST_MIN_COUNT + " and " + REMOTEMEPLIST_MAX_COUNT +
-                    " remote meps in an MA");
-        }
         for (MepId rmep:apiMa.remoteMepIdList()) {
             yamgMa.addToRemoteMeps(MepIdType.of(rmep.id()));
         }
@@ -486,7 +724,7 @@
         return yamgMa;
     }
 
-    private MaintenanceAssociationEndPoint buildYangMepFromApiMep(Mep mep)
+    private static MaintenanceAssociationEndPoint buildYangMepFromApiMep(Mep mep)
             throws CfmConfigException {
         MaintenanceAssociationEndPoint mepBuilder =
                                     new DefaultMaintenanceAssociationEndPoint();
@@ -515,14 +753,19 @@
 
     private MepEntry buildApiMepEntryFromYangMep(
             MaintenanceAssociationEndPoint yangMep, DeviceId deviceId,
-            MdId mdName, MaIdShort maName) throws CfmConfigException {
+            org.onosproject.yang.gen.v1.mseacfm.rev20160229.
+                    mseacfm.mefcfm.MaintenanceDomain replyMd,
+            org.onosproject.yang.gen.v1.mseacfm.rev20160229.
+                    mseacfm.mefcfm.maintenancedomain.MaintenanceAssociation replyMa)
+            throws CfmConfigException {
         MepId mepId = MepId.valueOf((short) yangMep.mepIdentifier().uint16());
         MepEntry.MepEntryBuilder builder = DefaultMepEntry.builder(mepId,
                 deviceId,
                 (yangMep.yangAutoPrefixInterface() == InterfaceEnum.ETH0) ?
                         PortNumber.portNumber(0) : PortNumber.portNumber(1),
                 MepDirection.DOWN_MEP, //Always down for EA1000
-                mdName, maName);
+                getApiMdIdFromYangMdName(replyMd.mdNameAndTypeCombo()),
+                getApiMaIdFromYangMaName(replyMa.maNameAndTypeCombo()));
 
         if (yangMep.loopback() != null) {
             MepLbEntryBuilder lbEntryBuilder = DefaultMepLbEntry.builder();
@@ -614,4 +857,10 @@
         }
         return rmepBuilder.build();
     }
+
+    private static short getMaNumericId(MaintenanceDomain md, MaIdShort maId) {
+        return md.maintenanceAssociationList().stream()
+                .filter(ma -> maId.equals(ma.maId()))
+                .findFirst().get().maNumericId();
+    }
 }
diff --git a/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/MseaCfmNetconfService.java b/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/MseaCfmNetconfService.java
index 1bc32ca..cbe41ba 100644
--- a/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/MseaCfmNetconfService.java
+++ b/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/MseaCfmNetconfService.java
@@ -18,6 +18,7 @@
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort;
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId;
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
 import org.onosproject.incubator.net.l2monitoring.soam.SoamId;
 import org.onosproject.netconf.DatastoreId;
 import org.onosproject.netconf.NetconfException;
@@ -29,6 +30,8 @@
 import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.transmitlinktrace.TransmitLinktraceOutput;
 import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.transmitloopback.TransmitLoopbackInput;
 
+import java.util.Optional;
+
 /**
  * Extension of mseaCfmService to include NETCONF sessions.
  *
@@ -65,6 +68,19 @@
     MseaCfm getMepFull(MdId mdId, MaIdShort maId, MepId mepId,
             NetconfSession session) throws NetconfException;
 
+
+    /**
+     * Returns set of all MepIds from one Md or Ma or all.
+     *
+     * @param mdIdOptional An MdId to filter by, or empty to select all
+     * @param maIdOptional An MaId to filter by, or empty to select all
+     * @param session An active NETCONF session
+     * @param targetDs one of running, candidate or startup
+     * @return mseaCfm
+     * @throws NetconfException if the session has any error
+     */
+    MseaCfm getMepIds(Optional<MdId> mdIdOptional, Optional<MaIdShort> maIdOptional,
+                      NetconfSession session, DatastoreId targetDs) throws NetconfException;
     /**
      * Returns attributes of DM.
      *
@@ -102,10 +118,52 @@
      * @param targetDs one of running, candidate or startup
      * @return Boolean to indicate success or failure
      * @throws NetconfException if the session has any error
+     * @throws CfmConfigException if the Cfm config has any error
      */
     boolean deleteMseaMep(MseaCfmOpParam mseaCfm, NetconfSession session,
-                            DatastoreId targetDs) throws NetconfException;
+                            DatastoreId targetDs) throws NetconfException, CfmConfigException;
 
+    /**
+     * Deletes named Ma of mseaCfm.
+     * Expects to see a list of Mas
+     *
+     * @param mseaCfm value of mseaCfm
+     * @param session An active NETCONF session
+     * @param targetDs one of running, candidate or startup
+     * @return Boolean to indicate success or failure
+     * @throws NetconfException if the session has any error
+     * @throws CfmConfigException if the Cfm config has any error
+     */
+    boolean deleteMseaMa(MseaCfmOpParam mseaCfm, NetconfSession session,
+                            DatastoreId targetDs) throws NetconfException, CfmConfigException;
+
+    /**
+     * Deletes a remote Mep from an MA.
+     * Expects one or more RMeps
+     *
+     * @param mseaCfm value of mseaCfm
+     * @param session An active NETCONF session
+     * @param targetDs one of running, candidate or startup
+     * @return Boolean to indicate success or failure
+     * @throws NetconfException if the session has any error
+     * @throws CfmConfigException if the Cfm config has any error
+     */
+    boolean deleteMseaMaRMep(MseaCfmOpParam mseaCfm, NetconfSession session,
+                         DatastoreId targetDs) throws NetconfException, CfmConfigException;
+
+    /**
+     * Deletes named Md of mseaCfm.
+     * Expects to see a list of Mds
+     *
+     * @param mseaCfm value of mseaCfm
+     * @param session An active NETCONF session
+     * @param targetDs one of running, candidate or startup
+     * @return Boolean to indicate success or failure
+     * @throws NetconfException if the session has any error
+     * @throws CfmConfigException if the Cfm config has any error
+     */
+    boolean deleteMseaMd(MseaCfmOpParam mseaCfm, NetconfSession session,
+                            DatastoreId targetDs) throws NetconfException, CfmConfigException;
 
     /**
      * Deletes named delay measurements of mseaCfm.
@@ -116,9 +174,10 @@
      * @param targetDs one of running, candidate or startup
      * @return Boolean to indicate success or failure
      * @throws NetconfException if the session has any error
+     * @throws CfmConfigException if the Cfm config has any error
      */
     boolean deleteMseaCfmDm(MseaCfmOpParam mseaCfm, NetconfSession session,
-                       DatastoreId targetDs) throws NetconfException;
+                       DatastoreId targetDs) throws NetconfException, CfmConfigException;
 
     /**
      * Service interface of transmitLoopback.
diff --git a/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/impl/MseaCfmManager.java b/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/impl/MseaCfmManager.java
index 695dfcb..ef40928 100644
--- a/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/impl/MseaCfmManager.java
+++ b/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/impl/MseaCfmManager.java
@@ -25,6 +25,7 @@
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort;
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId;
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
 import org.onosproject.incubator.net.l2monitoring.soam.SoamId;
 import org.onosproject.netconf.DatastoreId;
 import org.onosproject.netconf.NetconfException;
@@ -42,6 +43,7 @@
 import org.onosproject.yang.gen.v1.mseasoampm.rev20160229.mseasoampm.mefcfm.maintenancedomain.maintenanceassociation.maintenanceassociationendpoint.AugmentedMseaCfmMaintenanceAssociationEndPoint;
 import org.onosproject.yang.gen.v1.mseasoampm.rev20160229.mseasoampm.mefcfm.maintenancedomain.maintenanceassociation.maintenanceassociationendpoint.DefaultAugmentedMseaCfmMaintenanceAssociationEndPoint;
 import org.onosproject.yang.gen.v1.mseasoampm.rev20160229.mseasoampm.mefcfm.maintenancedomain.maintenanceassociation.maintenanceassociationendpoint.augmentedmseacfmmaintenanceassociationendpoint.delaymeasurements.DelayMeasurement;
+import org.onosproject.yang.gen.v1.mseatypes.rev20160229.mseatypes.MepIdType;
 import org.onosproject.yang.model.DefaultModelObjectData;
 import org.onosproject.yang.model.ModelConverter;
 import org.onosproject.yang.model.ModelObject;
@@ -54,6 +56,7 @@
 import org.onosproject.yang.runtime.DefaultCompositeStream;
 
 import java.io.ByteArrayInputStream;
+import java.util.Optional;
 import java.util.regex.Pattern;
 
 /**
@@ -64,10 +67,10 @@
 public class MseaCfmManager extends AbstractYangServiceImpl
     implements MseaCfmNetconfService {
 
-    public static final String MSEA_CFM = "org.onosproject.drivers.microsemi.yang.mseacfmservice";
+    private static final String MSEA_CFM = "org.onosproject.drivers.microsemi.yang.mseacfmservice";
 
-    public static final String MSEA_CFM_NS = "http://www.microsemi.com/microsemi-edge-assure/msea-cfm";
-    public static final String MSEA_CFM_PM_NS = "http://www.microsemi.com/microsemi-edge-assure/msea-soam-pm";
+    private static final String MSEA_CFM_NS = "http://www.microsemi.com/microsemi-edge-assure/msea-cfm";
+    private static final String MSEA_CFM_PM_NS = "http://www.microsemi.com/microsemi-edge-assure/msea-soam-pm";
 
     //FIXME Remove when the issue with Null bits on onos-yang-tools is sorted
     @Deprecated
@@ -77,6 +80,19 @@
     @Deprecated
     protected static final Pattern REGEX_EMPTY_LAST_DEFECT_SENT =
             Pattern.compile("(<msea-soam-fm:last-defect-sent)[ ]?(/>)", Pattern.DOTALL);
+    private static final String MEF_CFM = "mef-cfm";
+    private static final String MAINTENANCE_DOMAIN = "maintenance-domain";
+    public static final String ID = "id";
+    private static final String MAINTENANCE_ASSOCIATION = "maintenance-association";
+    private static final String TRANSMIT_LOOPBACK = "transmit-loopback";
+    private static final String ABORT_LOOPBACK = "abort-loopback";
+    private static final String MAINTENANCE_ASSOCIATION_END_POINT = "maintenance-association-end-point";
+    private static final String MEP_ID = "mep-id";
+    private static final String DELAY_MEASUREMENTS = "delay-measurements";
+    private static final String DELAY_MEASUREMENT = "delay-measurement";
+    private static final String DM_ID = "dm-id";
+    private static final String MEP_IDENTIFIER = "mep-identifier";
+    private static final String REMOTE_MEPS = "remote-meps";
 
     @Activate
     public void activate() {
@@ -153,6 +169,38 @@
     }
 
     @Override
+    public MseaCfm getMepIds(Optional<MdId> mdIdOptional, Optional<MaIdShort> maIdOptional,
+             NetconfSession session, DatastoreId targetDs) throws NetconfException {
+
+        ModelObjectData.Builder moQueryBldr = DefaultModelObjectData.builder();
+
+        ArrayList<AnnotatedNodeInfo> annotations = new ArrayList<>();
+        String xmlQueryStr = encodeMoToXmlStr(moQueryBldr.build(), annotations);
+
+        log.debug("Sending <get> for full MEP" +
+                " query on NETCONF session " + session.getSessionId() +
+                ":\n" + xmlQueryStr);
+
+        String xmlResult = session.get(xmlQueryStr, null);
+        xmlResult = removeRpcReplyData(xmlResult);
+        xmlResult = removeEmptyActiveDefects(xmlResult);
+        DefaultCompositeStream resultDcs = new DefaultCompositeStream(
+                null, new ByteArrayInputStream(xmlResult.getBytes()));
+        CompositeData compositeData = xSer.decode(resultDcs, yCtx);
+
+        ModelObjectData mod = ((ModelConverter) yangModelRegistry).createModel(compositeData.resourceData());
+
+        MseaCfmOpParam mseaCfm = new MseaCfmOpParam();
+        for (ModelObject mo:mod.modelObjects()) {
+            if (mo instanceof DefaultMefCfm) {
+                mseaCfm.mefCfm((DefaultMefCfm) mo);
+            }
+        }
+
+        return mseaCfm;
+    }
+
+    @Override
     public MseaCfm getSoamDm(MdId mdName, MaIdShort maName, MepId mepId,
                              SoamId dmId, DmEntryParts parts, NetconfSession session)
                     throws NetconfException {
@@ -190,38 +238,45 @@
 
     @Override
     public boolean deleteMseaCfmDm(MseaCfmOpParam mseaCfm, NetconfSession session,
-                            DatastoreId targetDs) throws NetconfException {
+                            DatastoreId targetDs) throws NetconfException, CfmConfigException {
 
+        if (mseaCfm.mefCfm() == null) {
+            throw new CfmConfigException("mefCfm object must be present before Meps can be added");
+        }
         ModelObjectData mseCfmDmList = DefaultModelObjectData.builder()
-                .addModelObject((ModelObject) mseaCfm).build();
+                .addModelObject(mseaCfm).build();
 
-        ArrayList anis = new ArrayList<AnnotatedNodeInfo>();
-        if (mseaCfm != null && mseaCfm.mefCfm() != null) {
-            for (MaintenanceDomain md:mseaCfm.mefCfm().maintenanceDomain()) {
-                for (MaintenanceAssociation ma:md.maintenanceAssociation()) {
-                    for (MaintenanceAssociationEndPoint mep:ma.maintenanceAssociationEndPoint()) {
-                        AugmentedMseaCfmMaintenanceAssociationEndPoint mepAugment =
-                            mep.augmentation(DefaultAugmentedMseaCfmMaintenanceAssociationEndPoint.class);
-                        if (mepAugment != null && mepAugment.delayMeasurements() != null) {
-                            for (DelayMeasurement dms:mepAugment.delayMeasurements().delayMeasurement()) {
-                                ResourceId.Builder ridBuilder = ResourceId.builder()
-                                        .addBranchPointSchema("/", null)
-                                        .addBranchPointSchema("mef-cfm", MSEA_CFM_NS)
-                                        .addBranchPointSchema("maintenance-domain", MSEA_CFM_NS)
-                                        .addKeyLeaf("id", MSEA_CFM_NS, md.id())
-                                        .addBranchPointSchema("maintenance-association", MSEA_CFM_NS)
-                                        .addKeyLeaf("id", MSEA_CFM_NS, ma.id())
-                                        .addBranchPointSchema("maintenance-association-end-point", MSEA_CFM_NS)
-                                        .addKeyLeaf("mep-id", MSEA_CFM_NS, mep.mepIdentifier())
-                                        .addBranchPointSchema("delay-measurements", MSEA_CFM_PM_NS)
-                                        .addBranchPointSchema("delay-measurement", MSEA_CFM_PM_NS)
-                                        .addKeyLeaf("dm-id", MSEA_CFM_PM_NS, mep.mepIdentifier());
-                                AnnotatedNodeInfo ani = DefaultAnnotatedNodeInfo.builder()
-                                        .resourceId(ridBuilder.build())
-                                        .addAnnotation(new DefaultAnnotation(NC_OPERATION, OP_DELETE))
-                                        .build();
-                                anis.add(ani);
-                            }
+        ArrayList<AnnotatedNodeInfo> anis = new ArrayList<>();
+        for (MaintenanceDomain md:mseaCfm.mefCfm().maintenanceDomain()) {
+            if (md.id() == 0) {
+                throw new CfmConfigException("An MD numeric ID must be given");
+            }
+            for (MaintenanceAssociation ma:md.maintenanceAssociation()) {
+                if (ma.id() == 0) {
+                    throw new CfmConfigException("An MA numeric ID must be given");
+                }
+                for (MaintenanceAssociationEndPoint mep:ma.maintenanceAssociationEndPoint()) {
+                    AugmentedMseaCfmMaintenanceAssociationEndPoint mepAugment =
+                        mep.augmentation(DefaultAugmentedMseaCfmMaintenanceAssociationEndPoint.class);
+                    if (mepAugment != null && mepAugment.delayMeasurements() != null) {
+                        for (DelayMeasurement dm:mepAugment.delayMeasurements().delayMeasurement()) {
+                            ResourceId.Builder ridBuilder = ResourceId.builder()
+                                    .addBranchPointSchema("/", null)
+                                    .addBranchPointSchema(MEF_CFM, MSEA_CFM_NS)
+                                    .addBranchPointSchema(MAINTENANCE_DOMAIN, MSEA_CFM_NS)
+                                    .addKeyLeaf(ID, MSEA_CFM_NS, md.id())
+                                    .addBranchPointSchema(MAINTENANCE_ASSOCIATION, MSEA_CFM_NS)
+                                    .addKeyLeaf(ID, MSEA_CFM_NS, ma.id())
+                                    .addBranchPointSchema(MAINTENANCE_ASSOCIATION_END_POINT, MSEA_CFM_NS)
+                                    .addKeyLeaf(MEP_ID, MSEA_CFM_NS, mep.mepIdentifier())
+                                    .addBranchPointSchema(DELAY_MEASUREMENTS, MSEA_CFM_PM_NS)
+                                    .addBranchPointSchema(DELAY_MEASUREMENT, MSEA_CFM_PM_NS)
+                                    .addKeyLeaf(DM_ID, MSEA_CFM_PM_NS, dm.dmId());
+                            AnnotatedNodeInfo ani = DefaultAnnotatedNodeInfo.builder()
+                                    .resourceId(ridBuilder.build())
+                                    .addAnnotation(new DefaultAnnotation(NC_OPERATION, OP_DELETE))
+                                    .build();
+                            anis.add(ani);
                         }
                     }
                 }
@@ -233,29 +288,38 @@
 
     @Override
     public boolean deleteMseaMep(MseaCfmOpParam mseaCfm, NetconfSession session,
-                                   DatastoreId targetDs) throws NetconfException {
+                                   DatastoreId targetDs) throws NetconfException, CfmConfigException {
 
+        if (mseaCfm.mefCfm() == null) {
+            throw new CfmConfigException("mefCfm object must be present before Meps can be added");
+        }
         ModelObjectData mseCfmMepList = DefaultModelObjectData.builder()
                 .addModelObject((ModelObject) mseaCfm.mefCfm()).build();
 
-        ArrayList anis = new ArrayList<AnnotatedNodeInfo>();
-        if (mseaCfm != null && mseaCfm.mefCfm() != null) {
-            for (MaintenanceDomain md:mseaCfm.mefCfm().maintenanceDomain()) {
-                for (MaintenanceAssociation ma:md.maintenanceAssociation()) {
-                    for (MaintenanceAssociationEndPoint mep:ma.maintenanceAssociationEndPoint()) {
-                        ResourceId.Builder ridBuilder = ResourceId.builder()
-                                .addBranchPointSchema("/", null)
-                                .addBranchPointSchema("mef-cfm", MSEA_CFM_NS)
-                                .addBranchPointSchema("maintenance-domain", MSEA_CFM_NS)
-                                .addBranchPointSchema("maintenance-association", MSEA_CFM_NS)
-                                .addBranchPointSchema("maintenance-association-end-point", MSEA_CFM_NS)
-                                .addKeyLeaf("mep-identifier", MSEA_CFM_NS, mep.mepIdentifier().uint16());
-                        AnnotatedNodeInfo ani = DefaultAnnotatedNodeInfo.builder()
-                                .resourceId(ridBuilder.build())
-                                .addAnnotation(new DefaultAnnotation(NC_OPERATION, OP_DELETE))
-                                .build();
-                        anis.add(ani);
-                    }
+        ArrayList<AnnotatedNodeInfo> anis = new ArrayList<>();
+        for (MaintenanceDomain md:mseaCfm.mefCfm().maintenanceDomain()) {
+            if (md.id() == 0) {
+                throw new CfmConfigException("An MD numeric ID must be given");
+            }
+            for (MaintenanceAssociation ma:md.maintenanceAssociation()) {
+                if (ma.id() == 0) {
+                    throw new CfmConfigException("An MA numeric ID must be given");
+                }
+                for (MaintenanceAssociationEndPoint mep:ma.maintenanceAssociationEndPoint()) {
+                    ResourceId.Builder ridBuilder = ResourceId.builder()
+                            .addBranchPointSchema("/", null)
+                            .addBranchPointSchema(MEF_CFM, MSEA_CFM_NS)
+                            .addBranchPointSchema(MAINTENANCE_DOMAIN, MSEA_CFM_NS)
+                            .addKeyLeaf(ID, MSEA_CFM_NS, md.id())
+                            .addBranchPointSchema(MAINTENANCE_ASSOCIATION, MSEA_CFM_NS)
+                            .addKeyLeaf(ID, MSEA_CFM_NS, ma.id())
+                            .addBranchPointSchema(MAINTENANCE_ASSOCIATION_END_POINT, MSEA_CFM_NS)
+                            .addKeyLeaf(MEP_IDENTIFIER, MSEA_CFM_NS, mep.mepIdentifier().uint16());
+                    AnnotatedNodeInfo ani = DefaultAnnotatedNodeInfo.builder()
+                            .resourceId(ridBuilder.build())
+                            .addAnnotation(new DefaultAnnotation(NC_OPERATION, OP_DELETE))
+                            .build();
+                    anis.add(ani);
                 }
             }
         }
@@ -263,6 +327,112 @@
         return setNetconfObject(mseCfmMepList, session, targetDs, anis);
     }
 
+    @Override
+    public boolean deleteMseaMa(MseaCfmOpParam mseaCfm, NetconfSession session,
+                                 DatastoreId targetDs) throws NetconfException, CfmConfigException {
+        if (mseaCfm.mefCfm() == null) {
+            throw new CfmConfigException("mefCfm object must be present before Meps can be added");
+        }
+
+        ModelObjectData mseCfmMepList = DefaultModelObjectData.builder()
+                .addModelObject((ModelObject) mseaCfm.mefCfm()).build();
+        ArrayList<AnnotatedNodeInfo> anis = new ArrayList<>();
+        for (MaintenanceDomain md:mseaCfm.mefCfm().maintenanceDomain()) {
+            if (md.id() == 0) {
+                throw new CfmConfigException("An MD numeric ID must be given");
+            }
+            for (MaintenanceAssociation ma:md.maintenanceAssociation()) {
+                if (ma.id() == 0) {
+                    throw new CfmConfigException("An MA numeric ID must be given");
+                }
+                ResourceId.Builder ridBuilder = ResourceId.builder()
+                        .addBranchPointSchema("/", null)
+                        .addBranchPointSchema(MEF_CFM, MSEA_CFM_NS)
+                        .addBranchPointSchema(MAINTENANCE_DOMAIN, MSEA_CFM_NS)
+                        .addKeyLeaf(ID, MSEA_CFM_NS, md.id())
+                        .addBranchPointSchema(MAINTENANCE_ASSOCIATION, MSEA_CFM_NS)
+                        .addKeyLeaf(ID, MSEA_CFM_NS, ma.id());
+
+                AnnotatedNodeInfo ani = DefaultAnnotatedNodeInfo.builder()
+                .resourceId(ridBuilder.build())
+                .addAnnotation(new DefaultAnnotation(NC_OPERATION, OP_DELETE))
+                .build();
+                anis.add(ani);
+            }
+        }
+
+        return setNetconfObject(mseCfmMepList, session, targetDs, anis);
+    }
+
+    @Override
+    public boolean deleteMseaMaRMep(MseaCfmOpParam mseaCfm, NetconfSession session,
+                                 DatastoreId targetDs) throws NetconfException, CfmConfigException {
+        if (mseaCfm.mefCfm() == null) {
+            throw new CfmConfigException("mefCfm object must be present before Meps can be added");
+        }
+
+        ModelObjectData mseCfmMepList = DefaultModelObjectData.builder()
+                .addModelObject((ModelObject) mseaCfm.mefCfm()).build();
+        ArrayList<AnnotatedNodeInfo> anis = new ArrayList<>();
+        for (MaintenanceDomain md:mseaCfm.mefCfm().maintenanceDomain()) {
+            if (md.id() == 0) {
+                throw new CfmConfigException("An MD numeric ID must be given");
+            }
+            for (MaintenanceAssociation ma:md.maintenanceAssociation()) {
+                if (ma.id() == 0) {
+                    throw new CfmConfigException("An MA numeric ID must be given");
+                }
+                for (MepIdType rmep:ma.remoteMeps()) {
+                    ResourceId.Builder ridBuilder = ResourceId.builder()
+                            .addBranchPointSchema("/", null)
+                            .addBranchPointSchema(MEF_CFM, MSEA_CFM_NS)
+                            .addBranchPointSchema(MAINTENANCE_DOMAIN, MSEA_CFM_NS)
+                            .addKeyLeaf(ID, MSEA_CFM_NS, md.id())
+                            .addBranchPointSchema(MAINTENANCE_ASSOCIATION, MSEA_CFM_NS)
+                            .addKeyLeaf(ID, MSEA_CFM_NS, ma.id())
+                            .addLeafListBranchPoint(REMOTE_MEPS, MSEA_CFM_NS,
+                                    rmep.uint16());
+                    AnnotatedNodeInfo ani = DefaultAnnotatedNodeInfo.builder()
+                            .resourceId(ridBuilder.build())
+                            .addAnnotation(new DefaultAnnotation(NC_OPERATION, OP_DELETE))
+                            .build();
+                    anis.add(ani);
+                }
+            }
+        }
+
+        return setNetconfObject(mseCfmMepList, session, targetDs, anis);
+    }
+
+
+    @Override
+    public boolean deleteMseaMd(MseaCfmOpParam mseaCfm, NetconfSession session,
+                                DatastoreId targetDs) throws NetconfException, CfmConfigException {
+        if (mseaCfm.mefCfm() == null) {
+            throw new CfmConfigException("mefCfm object must be present before Meps can be added");
+        }
+        ModelObjectData mseCfmMepList = DefaultModelObjectData.builder()
+                .addModelObject((ModelObject) mseaCfm.mefCfm()).build();
+
+        ArrayList<AnnotatedNodeInfo> anis = new ArrayList<>();
+        for (MaintenanceDomain md:mseaCfm.mefCfm().maintenanceDomain()) {
+            if (md.id() == 0) {
+                throw new CfmConfigException("An MD numeric ID must be given");
+            }
+            ResourceId.Builder ridBuilder = ResourceId.builder()
+                    .addBranchPointSchema("/", null)
+                    .addBranchPointSchema(MEF_CFM, MSEA_CFM_NS)
+                    .addBranchPointSchema(MAINTENANCE_DOMAIN, MSEA_CFM_NS)
+                    .addKeyLeaf(ID, MSEA_CFM_NS, md.id());
+            AnnotatedNodeInfo ani = DefaultAnnotatedNodeInfo.builder()
+                    .resourceId(ridBuilder.build())
+                    .addAnnotation(new DefaultAnnotation(NC_OPERATION, OP_DELETE))
+                    .build();
+            anis.add(ani);
+        }
+
+        return setNetconfObject(mseCfmMepList, session, targetDs, anis);
+    }
 
     /**
      * Call RPCs on the device through NETCONF.
@@ -275,7 +445,7 @@
                 .addModelObject((ModelObject) inputVar).build();
 
         customRpcNetconf(transLoopbackMo,
-                "transmit-loopback", session);
+                TRANSMIT_LOOPBACK, session);
     }
 
     @Override
@@ -284,7 +454,7 @@
         ModelObjectData abortLoopbackMo = DefaultModelObjectData.builder()
                 .addModelObject((ModelObject) inputVar).build();
 
-        customRpcNetconf(abortLoopbackMo, "abort-loopback", session);
+        customRpcNetconf(abortLoopbackMo, ABORT_LOOPBACK, session);
     }
 
     @Override
@@ -314,15 +484,21 @@
         rpc.append("xmlns:msea-soam-pm=\"http://www.microsemi.com/microsemi-edge-assure/msea-soam-pm\">\n");
         rpc.append("<maintenance-domain>\n");
         rpc.append("<id/>\n");
-        rpc.append("<name>" + mdId.mdName() + "</name>\n");
+        rpc.append("<name>");
+        rpc.append(mdId.mdName());
+        rpc.append("</name>\n");
         rpc.append("<maintenance-association>\n");
         rpc.append("<id/>\n");
-        rpc.append("<name>" + maId.maName() + "</name>\n");
+        rpc.append("<name>");
+        rpc.append(maId.maName());
+        rpc.append("</name>\n");
         rpc.append("<ccm-interval>10ms</ccm-interval>\n");
         rpc.append("<remote-meps/>\n");
         rpc.append("<component-list/>\n");
         rpc.append("<maintenance-association-end-point>\n");
-        rpc.append("<mep-identifier>" + mepId.id() + "</mep-identifier>\n");
+        rpc.append("<mep-identifier>");
+        rpc.append(mepId.id());
+        rpc.append("</mep-identifier>\n");
         rpc.append("<mac-address/>\n");
         rpc.append("<remote-mep-database>\n");
         rpc.append("<remote-mep>\n");
@@ -356,12 +532,18 @@
         rpc.append("xmlns:msea-soam-pm=\"http://www.microsemi.com/microsemi-edge-assure/msea-soam-pm\">\n");
         rpc.append("<maintenance-domain>\n");
         rpc.append("<id/>\n");
-        rpc.append("<name>" + mdId.mdName() + "</name>\n");
+        rpc.append("<name>");
+        rpc.append(mdId.mdName());
+        rpc.append("</name>\n");
         rpc.append("<maintenance-association>\n");
         rpc.append("<id/>\n");
-        rpc.append("<name>" + maId.maName() + "</name>\n");
+        rpc.append("<name>");
+        rpc.append(maId.maName());
+        rpc.append("</name>\n");
         rpc.append("<maintenance-association-end-point>\n");
-        rpc.append("<mep-identifier>" + mepId.id() + "</mep-identifier>\n");
+        rpc.append("<mep-identifier>");
+        rpc.append(mepId.id());
+        rpc.append("</mep-identifier>\n");
         rpc.append("<interface/>\n");
         //Direction will always be DOWN for EA1000
         rpc.append("<primary-vid/>\n");
@@ -396,16 +578,24 @@
         rpc.append("xmlns:msea-soam-pm=\"http://www.microsemi.com/microsemi-edge-assure/msea-soam-pm\">\n");
         rpc.append("<maintenance-domain>\n");
         rpc.append("<id/>\n");
-        rpc.append("<name>" + mdId.mdName() + "</name>\n");
+        rpc.append("<name>");
+        rpc.append(mdId.mdName());
+        rpc.append("</name>\n");
         rpc.append("<maintenance-association>\n");
         rpc.append("<id/>\n");
-        rpc.append("<name>" + maId.maName() + "</name>\n");
+        rpc.append("<name>");
+        rpc.append(maId.maName());
+        rpc.append("</name>\n");
         rpc.append("<maintenance-association-end-point>\n");
-        rpc.append("<mep-identifier>" + mepId.id() + "</mep-identifier>\n");
+        rpc.append("<mep-identifier>");
+        rpc.append(mepId.id());
+        rpc.append("</mep-identifier>\n");
         if (dmId != null) {
             rpc.append("<msea-soam-pm:delay-measurements>");
             rpc.append("<msea-soam-pm:delay-measurement>\n");
-            rpc.append("<msea-soam-pm:dm-id>" + dmId.id() + "</msea-soam-pm:dm-id>\n");
+            rpc.append("<msea-soam-pm:dm-id>");
+            rpc.append(dmId.id());
+            rpc.append("</msea-soam-pm:dm-id>\n");
             rpc.append("<msea-soam-pm:mep-id/>");
             rpc.append("<msea-soam-pm:mac-address/>");
             rpc.append("<msea-soam-pm:administrative-state/>\n");
@@ -445,9 +635,15 @@
         StringBuilder rpc = new StringBuilder();
 
         rpc.append("<abort-loopback xmlns=\"http://www.microsemi.com/microsemi-edge-assure/msea-cfm\">");
-        rpc.append("<maintenance-domain>" + mdId + "</maintenance-domain>");
-        rpc.append("<maintenance-association>" + maId + "</maintenance-association>");
-        rpc.append("<maintenance-association-end-point>" + mepId + "</maintenance-association-end-point>");
+        rpc.append("<maintenance-domain>");
+        rpc.append(mdId);
+        rpc.append("</maintenance-domain>");
+        rpc.append("<maintenance-association>");
+        rpc.append(maId);
+        rpc.append("</maintenance-association>");
+        rpc.append("<maintenance-association-end-point>");
+        rpc.append(mepId);
+        rpc.append("</maintenance-association-end-point>");
         rpc.append("</abort-loopback>");
 
         return rpc.toString();
diff --git a/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/impl/MseaSaFilteringManager.java b/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/impl/MseaSaFilteringManager.java
index f9ed7a1..78c6315 100644
--- a/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/impl/MseaSaFilteringManager.java
+++ b/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/impl/MseaSaFilteringManager.java
@@ -119,7 +119,7 @@
                 reply.sourceIpaddressFiltering((SourceIpaddressFiltering) mo);
             }
         }
-        if (reply != null && reply.sourceIpaddressFiltering() != null &&
+        if (reply.sourceIpaddressFiltering() != null &&
                 reply.sourceIpaddressFiltering().interfaceEth0() != null) {
             return reply.sourceIpaddressFiltering().interfaceEth0().sourceAddressRange();
         } else {
diff --git a/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/utils/CeVlanMapUtils.java b/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/utils/CeVlanMapUtils.java
index 8b9309c..76534d0 100644
--- a/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/utils/CeVlanMapUtils.java
+++ b/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/utils/CeVlanMapUtils.java
@@ -155,7 +155,7 @@
     public static String removeZeroIfPossible(String existingMap) {
         if (existingMap == null || existingMap.isEmpty()) {
             return "0";
-        } else if (existingMap == "0") {
+        } else if ("0".equals(existingMap)) {
             return existingMap;
         }
         return removeFromCeVlanMap(existingMap, (short) 0);
diff --git a/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/utils/MaNameUtil.java b/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/utils/MaNameUtil.java
index 16e9686..e18abe6 100644
--- a/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/utils/MaNameUtil.java
+++ b/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/utils/MaNameUtil.java
@@ -30,11 +30,14 @@
 import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.maintenanceassociation.manameandtypecombo.DefaultNameRfc2685VpnId;
 import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.maintenanceassociation.manameandtypecombo.DefaultNameUint16;
 import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.maintenanceassociation.manameandtypecombo.DefaultNameY1731Icc;
+import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.maintenanceassociation.manameandtypecombo.NameCharacterString;
 import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.maintenanceassociation.manameandtypecombo.nameprimaryvid.NamePrimaryVidUnion;
 import org.onosproject.yang.gen.v1.mseatypes.rev20160229.mseatypes.Identifier45;
 
 /**
- * This is a workaround for Checkstyle issue.
+ * Utility for translating between Maintenance Association names in the CFM API model and the device YANG.
+ *
+ * This has to be in a separate file as a workaround for Checkstyle issue.
  * https://github.com/checkstyle/checkstyle/issues/3850
  * There are two types of DefaultNameCharacterString - one for MA and another for MD
  * Putting both together in a file means that the full path has to be given which
@@ -46,6 +49,12 @@
         //Hidden
     }
 
+    /**
+     * Convert CFM API MA identifier to the YANG model MA identifier.
+     * @param maId Maintenance Association ID in CFM API
+     * @return Maintenance Association ID in YANG API
+     * @throws CfmConfigException If there's a problem with the name
+     */
     public static MaNameAndTypeCombo getYangMaNameFromApiMaId(MaIdShort maId)
             throws CfmConfigException {
         MaNameAndTypeCombo maName;
@@ -70,4 +79,52 @@
         }
         return maName;
     }
+
+    /**
+     * Convert YANG API MA identifier to the CFM API MA identifier.
+     * @param nameAndTypeCombo Maintenance Association ID in YANG API
+     * @return Maintenance Association ID in CFM API
+     */
+    public static MaIdShort getApiMaIdFromYangMaName(MaNameAndTypeCombo nameAndTypeCombo) {
+        MaIdShort maId;
+        if (nameAndTypeCombo instanceof DefaultNameCharacterString) {
+            maId = MaIdCharStr.asMaId(
+                    ((DefaultNameCharacterString) nameAndTypeCombo).name().string());
+        } else if (nameAndTypeCombo instanceof DefaultNamePrimaryVid) {
+            if (((DefaultNamePrimaryVid) nameAndTypeCombo).namePrimaryVid().enumeration() != null) {
+                maId = MaIdPrimaryVid.asMaId(
+                        ((DefaultNamePrimaryVid) nameAndTypeCombo).namePrimaryVid().enumeration().name());
+            } else if (((DefaultNamePrimaryVid) nameAndTypeCombo).namePrimaryVid().vlanIdType() != null) {
+                maId = MaIdPrimaryVid.asMaId(
+                        ((DefaultNamePrimaryVid) nameAndTypeCombo).namePrimaryVid().vlanIdType().uint16());
+            } else {
+                throw new IllegalArgumentException("Unexpected primaryVid for " +
+                        "MaNameAndTypeCombo: " + nameAndTypeCombo.toString());
+            }
+        } else if (nameAndTypeCombo instanceof DefaultNameUint16) {
+            maId = MaId2Octet.asMaId(((DefaultNameUint16) nameAndTypeCombo).nameUint16());
+
+        } else if (nameAndTypeCombo instanceof DefaultNameRfc2685VpnId) {
+            maId = MaIdRfc2685VpnId.asMaIdHex(
+                    HexString.toHexString(
+                            ((DefaultNameRfc2685VpnId) nameAndTypeCombo).nameRfc2685VpnId()));
+        } else if (nameAndTypeCombo instanceof DefaultNameY1731Icc) {
+            maId = MaIdIccY1731.asMaId(((DefaultNameY1731Icc) nameAndTypeCombo).nameY1731Icc().string());
+
+        } else {
+            throw new IllegalArgumentException("Unexpected type for " +
+                    "MaNameAndTypeCombo: " + nameAndTypeCombo.toString());
+        }
+
+        return maId;
+    }
+
+    /**
+     * Cast the YANG generic type of MaNameAndTypeCombo specifically to char string.
+     * @param maName a YANG generic MaNameAndTypeCombo
+     * @return a YANG specific MaNameAndTypeCombo for Char string
+     */
+    public static NameCharacterString cast(MaNameAndTypeCombo maName) {
+        return (NameCharacterString) maName;
+    }
 }
diff --git a/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/utils/MdNameUtil.java b/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/utils/MdNameUtil.java
new file mode 100644
index 0000000..ba0cc96
--- /dev/null
+++ b/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/utils/MdNameUtil.java
@@ -0,0 +1,139 @@
+/*
+ * 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.drivers.microsemi.yang.utils;
+
+import org.onlab.packet.IpAddress;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdDomainName;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdMacUint;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdNone;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
+import org.onosproject.yang.gen.v1.ietfinettypes.rev20130715.ietfinettypes.DomainName;
+import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.MdNameAndTypeCombo;
+import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.mdnameandtypecombo.DefaultMacAddressAndUint;
+import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.mdnameandtypecombo.DefaultNameCharacterString;
+import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.mdnameandtypecombo.DefaultNameDomainName;
+import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.mdnameandtypecombo.DefaultNameNone;
+import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.mdnameandtypecombo.MacAddressAndUint;
+import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.mdnameandtypecombo.NameCharacterString;
+import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.mdnameandtypecombo.namedomainname.NameDomainNameUnion;
+import org.onosproject.yang.gen.v1.mseatypes.rev20160229.mseatypes.Identifier45;
+import org.onosproject.yang.gen.v1.mseatypes.rev20160229.mseatypes.MacAddressAndUintStr;
+
+/**
+ * Utility for translating between Maintenance Domain names in the CFM API model and the device YANG.
+ *
+ * This has to be in a separate file as a workaround for Checkstyle issue.
+ * https://github.com/checkstyle/checkstyle/issues/3850
+ * There are two types of DefaultNameCharacterString - one for MA and another for MD
+ * Putting both together in a file means that the full path has to be given which
+ * will then fail checkstyle
+ */
+public final class MdNameUtil {
+
+    private MdNameUtil() {
+        //Hidden
+    }
+
+    /**
+     * Convert CFM API MD identifier to the YANG model MD identifier.
+     * @param mdId Maintenance Domain ID in CFM API
+     * @return Maintenance Domain ID in YANG API
+     * @throws CfmConfigException If there's a problem with the name
+     */
+    public static MdNameAndTypeCombo getYangMdNameFromApiMdId(MdId mdId)
+            throws CfmConfigException {
+        MdNameAndTypeCombo mdName;
+        if (mdId instanceof MdIdDomainName) {
+            boolean isIpAddr = false;
+            try {
+                if (IpAddress.valueOf(mdId.mdName()) != null) {
+                    isIpAddr = true;
+                }
+            } catch (IllegalArgumentException e) {
+                //continue
+            }
+            if (isIpAddr) {
+                mdName = new DefaultNameDomainName();
+                ((DefaultNameDomainName) mdName).nameDomainName(NameDomainNameUnion.of(
+                        org.onosproject.yang.gen.v1.ietfinettypes.rev20130715.ietfinettypes.
+                                IpAddress.fromString(mdId.mdName())));
+            } else {
+                mdName = new DefaultNameDomainName();
+                ((DefaultNameDomainName) mdName).nameDomainName(NameDomainNameUnion
+                        .of(DomainName.fromString(mdId.mdName())));
+            }
+        } else if (mdId instanceof MdIdMacUint) {
+            mdName = new DefaultMacAddressAndUint();
+            ((DefaultMacAddressAndUint) mdName).nameMacAddressAndUint(MacAddressAndUintStr.fromString(mdId.mdName()));
+        } else if (mdId instanceof MdIdNone) {
+            mdName = new DefaultNameNone();
+        } else if (mdId instanceof MdIdCharStr) {
+            mdName = new DefaultNameCharacterString();
+            ((DefaultNameCharacterString) mdName).name(Identifier45.fromString(mdId.mdName()));
+        } else {
+            throw new CfmConfigException("Unexpected error creating MD " +
+                    mdId.getClass().getSimpleName());
+        }
+        return mdName;
+    }
+
+    /**
+     * Convert YANG API MD identifier to the CFM API MD identifier.
+     * @param nameAndTypeCombo Maintenance Domain ID in YANG API
+     * @return Maintenance Domain ID in CFM API
+     */
+    public static MdId getApiMdIdFromYangMdName(MdNameAndTypeCombo nameAndTypeCombo) {
+        MdId mdId;
+        if (nameAndTypeCombo instanceof DefaultNameDomainName) {
+            NameDomainNameUnion domainName =
+                    ((DefaultNameDomainName) nameAndTypeCombo).nameDomainName();
+            if (domainName.ipAddress() != null) {
+                mdId = MdIdDomainName.asMdId(domainName.ipAddress().toString());
+            } else if (domainName.domainName() != null) {
+                mdId = MdIdDomainName.asMdId(domainName.domainName().string());
+            } else {
+                throw new IllegalArgumentException("Unexpected domainName for " +
+                        "MdNameAndTypeCombo: " + nameAndTypeCombo.toString());
+            }
+        } else if (nameAndTypeCombo instanceof DefaultNameCharacterString) {
+            mdId = MdIdCharStr.asMdId(
+                    ((NameCharacterString) nameAndTypeCombo).name().string());
+
+        } else if (nameAndTypeCombo instanceof DefaultMacAddressAndUint) {
+            mdId = MdIdMacUint.asMdId(
+                    ((MacAddressAndUint) nameAndTypeCombo).nameMacAddressAndUint().string());
+
+        } else if (nameAndTypeCombo instanceof DefaultNameNone) {
+            mdId = MdIdNone.asMdId();
+        } else {
+            throw new IllegalArgumentException("Unexpected type for " +
+                    "MdNameAndTypeCombo: " + nameAndTypeCombo.toString());
+        }
+
+        return mdId;
+    }
+
+    /**
+     * Cast the YANG generic type of MdNameAndTypeCombo specifically to char string.
+     * @param maName a YANG generic MdNameAndTypeCombo
+     * @return a YANG specific MdNameAndTypeCombo for Char string
+     */
+    public static NameCharacterString cast(MdNameAndTypeCombo maName) {
+        return (NameCharacterString) maName;
+    }
+}
diff --git a/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/EA1000CfmMepProgrammableTest.java b/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/EA1000CfmMepProgrammableTest.java
index 20e80a1..1f87634 100644
--- a/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/EA1000CfmMepProgrammableTest.java
+++ b/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/EA1000CfmMepProgrammableTest.java
@@ -20,11 +20,13 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.onosproject.drivers.microsemi.yang.utils.MdNameUtil.getYangMdNameFromApiMdId;
 
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
 import org.onosproject.drivers.microsemi.yang.utils.MaNameUtil;
+import org.onosproject.drivers.microsemi.yang.utils.MdNameUtil;
 import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMepLbCreate;
 import org.onosproject.incubator.net.l2monitoring.cfm.Mep.Priority;
 import org.onosproject.incubator.net.l2monitoring.cfm.MepEntry;
@@ -38,21 +40,24 @@
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr;
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
 import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepProgrammable;
 import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.MdNameAndTypeCombo;
 import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.maintenanceassociation.MaNameAndTypeCombo;
 
 import java.util.BitSet;
+import java.util.Optional;
 
 /**
  * Test of the CFM implementation on EA1000 through the incubator/net/l2monitoring interface.
  */
 public class EA1000CfmMepProgrammableTest {
-    EA1000CfmMepProgrammable cfmProgrammable;
     public static final MdId MD_ID_1 = MdIdCharStr.asMdId("md-1");
     public static final MaIdShort MA_ID_11 = MaIdCharStr.asMaId("ma-1-1");
     public static final MepId MEP_111 = MepId.valueOf((short) 1);
     public static final MepId MEP_112 = MepId.valueOf((short) 2);
 
+    private CfmMepProgrammable cfmProgrammable;
+
     @Before
     public void setUp() throws Exception {
         cfmProgrammable = new EA1000CfmMepProgrammable();
@@ -67,12 +72,6 @@
         fail("Not yet implemented");
     }
 
-    @Ignore
-    @Test
-    public void testGetAllMeps() {
-        fail("Not yet implemented");
-    }
-
     @Test
     public void testGetMep() throws CfmConfigException {
         MepEntry mepEntry = cfmProgrammable.getMep(MD_ID_1, MA_ID_11, MEP_111);
@@ -143,7 +142,107 @@
      */
     @Test
     public void testDeleteMep() throws CfmConfigException {
-        assertTrue(cfmProgrammable.deleteMep(MD_ID_1, MA_ID_11, MEP_111));
+        assertTrue(cfmProgrammable.deleteMep(MD_ID_1, MA_ID_11, MEP_111, Optional.empty()));
+    }
+
+    /**
+     * Create the MD md-1 on the device.
+     * This will retrieve the MD from the MockCfmMdService and will create it
+     * and its MA on the device
+     * Depends on sampleXmlRegexCreateMseaCfmMa
+     */
+    @Test
+    public void testCreateMaintenanceDomainOnDevice() throws CfmConfigException {
+        boolean success =
+                cfmProgrammable.createMdOnDevice(MdIdCharStr.asMdId("md-1"));
+        assertTrue(success);
+    }
+
+    /**
+     * Create the MD md-2 on the device.
+     * This will retrieve the MD from the MockCfmMdService and will create it on
+     * the device. This MD has no MA
+     * Depends on sampleXmlRegexCreateMseaCfmMa
+     */
+    @Test
+    public void testCreateMaintenanceDomainOnDevice2() throws CfmConfigException {
+        boolean success =
+                cfmProgrammable.createMdOnDevice(MdIdCharStr.asMdId("md-2"));
+        assertTrue(success);
+    }
+
+    /**
+     * Delete the MD md-1 on the device.
+     * This will retrieve the MD from the MockCfmMdService and will delete it on
+     * the device.
+     * Depends on sampleXmlRegexCreateMseaCfmMa
+     */
+    @Test
+    public void testDeleteMaintenanceDomainOnDevice() throws CfmConfigException {
+        boolean success =
+                cfmProgrammable.deleteMdOnDevice(MdIdCharStr.asMdId("md-1"), Optional.empty());
+        assertTrue(success);
+    }
+
+
+    /**
+     * Create the MA ma-1-1 on the device.
+     * This will retrieve the MA from the MockCfmMdService and will create it
+     * on the device under md-1
+     * Depends on sampleXmlRegexCreateMseaCfmMa
+     */
+    @Test
+    public void testCreateMaintenanceAssociationOnDevice() throws CfmConfigException {
+        boolean success =
+                cfmProgrammable.createMaOnDevice(
+                        MdIdCharStr.asMdId("md-1"), MaIdCharStr.asMaId("ma-1-1"));
+        assertTrue(success);
+    }
+
+    /**
+     * Delete the MD md-1 on the device.
+     * This will retrieve the MD from the MockCfmMdService and will delete it on
+     * the device.
+     * Depends on sampleXmlRegexCreateMseaCfmMa
+     */
+    @Test
+    public void testDeleteMaintenanceAssociationOnDevice() throws CfmConfigException {
+        boolean success =
+                cfmProgrammable.deleteMaOnDevice(
+                        MdIdCharStr.asMdId("md-1"),
+                        MaIdCharStr.asMaId("ma-1-1"),
+                        Optional.empty());
+        assertTrue(success);
+    }
+
+    /**
+     * Create the Remote Mep 10001 in ma-1-1 on the device.
+     * This will retrieve the MA from the MockCfmMdService and will create the
+     * new remote mep under it on the device
+     * Depends on sampleXmlRegexCreateMseaCfmMa
+     */
+    @Test
+    public void testCreateRemoteMepOnDevice() throws CfmConfigException {
+        boolean success =
+                cfmProgrammable.createMaRemoteMepOnDevice(
+                        MdIdCharStr.asMdId("md-1"), MaIdCharStr.asMaId("ma-1-1"),
+                        MepId.valueOf((short) 1001));
+        assertTrue(success);
+    }
+
+    /**
+     * Delete the Remote Mep 1002 in ma-1-1 on the device.
+     * This will retrieve the MA from the MockCfmMdService and will delete the
+     * existing remote mep under it on the device
+     * Depends on sampleXmlRegexCreateMseaCfmMa
+     */
+    @Test
+    public void testDeleteRemoteMepOnDevice() throws CfmConfigException {
+        boolean success =
+                cfmProgrammable.deleteMaRemoteMepOnDevice(
+                        MdIdCharStr.asMdId("md-1"), MaIdCharStr.asMaId("ma-1-1"),
+                        MepId.valueOf((short) 1001));
+        assertTrue(success);
     }
 
     /**
@@ -167,25 +266,21 @@
         cfmProgrammable.abortLoopback(MD_ID_1, MA_ID_11, MEP_111);
     }
 
-//    @Test
-//    public void testTransmitLinktrace() {
-//        fail("Not yet implemented");
-//    }
+    @Ignore
+    @Test
+    public void testTransmitLinktrace() {
+        fail("Not yet implemented");
+    }
 
     @Test
     public void testGetYangMdNameFromApiMdId() throws CfmConfigException {
-        MdNameAndTypeCombo name = EA1000CfmMepProgrammable
-                .getYangMdNameFromApiMdId(MdIdCharStr.asMdId("md-1"));
+        MdNameAndTypeCombo name = getYangMdNameFromApiMdId(MdIdCharStr.asMdId("md-1"));
 
         assertEquals(org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm
                 .maintenancedomain.mdnameandtypecombo
                 .DefaultNameCharacterString.class, name.getClass());
 
-//There's a problem with checkstyle for typecast on really long paths
-//        assertEquals("md-1", ((org.onosproject.yang.gen.v1.http.www.microsemi.com
-//                .microsemi.edge.assure.msea.cfm.rev20160229.mseacfm.mefcfm
-//                .maintenancedomain.mdnameandtypecombo
-//                .DefaultNameCharacterString) name).name().string());
+        assertEquals("md-1", MdNameUtil.cast(name).name().string());
     }
 
     @Test
@@ -196,11 +291,6 @@
                 .maintenancedomain.maintenanceassociation.manameandtypecombo
                 .DefaultNameCharacterString.class, name.getClass());
 
-//There's a problem with checkstyle for typecast on really long paths
-//        assertEquals("ma-1-1", ((org.onosproject.yang.gen.v1.http.www.microsemi.com
-//                .microsemi.edge.assure.msea.cfm.rev20160229.mseacfm.mefcfm
-//                .maintenancedomain.maintenanceassociation.manameandtypecombo
-//                .DefaultNameCharacterString) name).name().string());
+        assertEquals("ma-1-1", MaNameUtil.cast(name).name().string());
     }
-
 }
diff --git a/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/MockEa1000DriverHandler.java b/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/MockEa1000DriverHandler.java
index 58a8a5b..742f8c9 100644
--- a/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/MockEa1000DriverHandler.java
+++ b/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/MockEa1000DriverHandler.java
@@ -20,6 +20,7 @@
 
 import org.onosproject.core.CoreService;
 import org.onosproject.drivers.microsemi.yang.MockCfmMdService;
+import org.onosproject.drivers.microsemi.yang.MockCfmMepService;
 import org.onosproject.drivers.microsemi.yang.MockMseaCfmManager;
 import org.onosproject.drivers.microsemi.yang.MockMseaSaFilteringManager;
 import org.onosproject.drivers.microsemi.yang.MockMseaUniEvcServiceManager;
@@ -31,6 +32,7 @@
 import org.onosproject.drivers.netconf.MockNetconfController;
 import org.onosproject.drivers.netconf.MockNetconfDevice;
 import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMdService;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepService;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.driver.Behaviour;
 import org.onosproject.net.driver.DefaultDriver;
@@ -59,6 +61,7 @@
     private MockMseaUniEvcServiceManager mseaUniEvcService;
     private MockMseaCfmManager mseaCfmService;
     private MockCfmMdService mockMdService;
+    private MockCfmMepService mockMepService;
     private CoreService coreService;
 
     public MockEa1000DriverHandler() throws NetconfException {
@@ -92,6 +95,9 @@
         mockMdService = new MockCfmMdService();
         mockMdService.activate();
 
+        mockMepService = new MockCfmMepService();
+        mockMepService.activate();
+
         coreService = new MockCoreService();
         coreService.registerApplication(MICROSEMI_DRIVERS);
     }
@@ -131,6 +137,10 @@
 
         } else if (serviceClass.equals(CfmMdService.class)) {
             return (T) mockMdService;
+
+        } else if (serviceClass.equals(CfmMepService.class)) {
+            return (T) mockMepService;
+
         }
 
         return null;
diff --git a/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/yang/MockCfmMdService.java b/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/yang/MockCfmMdService.java
index ba6d656..3d0fb5f 100644
--- a/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/yang/MockCfmMdService.java
+++ b/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/yang/MockCfmMdService.java
@@ -15,6 +15,8 @@
  */
 package org.onosproject.drivers.microsemi.yang;
 
+import org.onlab.packet.VlanId;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultComponent;
 import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMaintenanceAssociation;
 import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMaintenanceDomain;
 import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceAssociation;
@@ -22,6 +24,7 @@
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdCharStr;
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId;
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
 import org.onosproject.incubator.net.l2monitoring.cfm.impl.CfmMdManager;
 import static org.easymock.EasyMock.*;
 import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
@@ -29,6 +32,9 @@
 
 import java.util.Optional;
 
+/**
+ * Supports testing of services that reply on the CfmMdService.
+ */
 public class MockCfmMdService extends CfmMdManager {
 
     @Override
@@ -40,6 +46,11 @@
                     .builder(MaIdCharStr.asMaId("ma-1-1"), 6)
                     .maNumericId((short) 1)
                     .ccmInterval(MaintenanceAssociation.CcmInterval.INTERVAL_3MS)
+                    .addToRemoteMepIdList(MepId.valueOf((short) 10))
+                    .addToRemoteMepIdList(MepId.valueOf((short) 20))
+                    .addToComponentList(
+                            DefaultComponent.builder(1)
+                                    .addToVidList(VlanId.vlanId((short) 101)).build())
                     .build();
 
             MdId md1Name = MdIdCharStr.asMdId("md-1");
@@ -50,14 +61,25 @@
                     .addToMaList(ma)
                     .build();
 
+            MdId md2Name = MdIdCharStr.asMdId("md-2");
+            MaintenanceDomain md2 = DefaultMaintenanceDomain
+                    .builder(md1Name)
+                    .mdNumericId((short) 2)
+                    .mdLevel(MaintenanceDomain.MdLevel.LEVEL2)
+                    .build();
+
             expect(store.createUpdateMaintenanceDomain(md1))
                     .andReturn(true);
+            expect(store.createUpdateMaintenanceDomain(md2))
+                    .andReturn(true);
             expect(store.getMaintenanceDomain(md1Name))
                     .andReturn(Optional.of(md1)).anyTimes();
+            expect(store.getMaintenanceDomain(md2Name))
+                    .andReturn(Optional.of(md2)).anyTimes();
             replay(store);
 
         } catch (CfmConfigException e) {
-            throw new IllegalArgumentException("Error creating Md md-1 for test");
+            throw new IllegalArgumentException("Error creating MDs for test", e);
         }
     }
 }
diff --git a/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/yang/MockCfmMepService.java b/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/yang/MockCfmMepService.java
new file mode 100644
index 0000000..c00fc4e
--- /dev/null
+++ b/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/yang/MockCfmMepService.java
@@ -0,0 +1,137 @@
+/*
+ * 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.drivers.microsemi.yang;
+
+import org.onlab.junit.TestUtils;
+import org.onlab.packet.ChassisId;
+import org.onosproject.common.event.impl.TestEventDispatcher;
+import org.onosproject.core.CoreServiceAdapter;
+import org.onosproject.core.IdGenerator;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMep;
+import org.onosproject.incubator.net.l2monitoring.cfm.Mep;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepKeyId;
+import org.onosproject.incubator.net.l2monitoring.cfm.impl.CfmMdManager;
+import org.onosproject.incubator.net.l2monitoring.cfm.impl.CfmMepManager;
+import org.onosproject.incubator.net.l2monitoring.cfm.impl.TestCfmMepProgrammable;
+import org.onosproject.incubator.net.l2monitoring.cfm.impl.TestDeviceDiscoveryBehavior;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepProgrammable;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.MepStore;
+import org.onosproject.incubator.net.l2monitoring.soam.SoamDmProgrammable;
+import org.onosproject.incubator.net.l2monitoring.soam.impl.TestSoamDmProgrammable;
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DefaultDevice;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceDescriptionDiscovery;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.Behaviour;
+import org.onosproject.net.driver.DefaultDriver;
+import org.onosproject.net.driver.Driver;
+import org.onosproject.net.driver.DriverService;
+import org.onosproject.net.provider.ProviderId;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.onosproject.net.NetTestTools.injectEventDispatcher;
+
+/**
+ * Supports testing of services that reply on the CfmMepService.
+ */
+public class MockCfmMepService extends CfmMepManager {
+    private static final String TEST_MFR = "testMfr";
+    private static final String TEST_HW_VERSION = "testHwVersion";
+    private static final String TEST_SW_VERSION = "testSwVersion";
+    private static final String TEST_SN = "testSn";
+    private static final String TEST_DRIVER = "testDriver";
+    protected static final DeviceId DEVICE_ID1 = DeviceId.deviceId("netconf:1.2.3.4:830");
+
+
+    private final DriverService driverService = createMock(DriverService.class);
+
+    private Device device1;
+    private Driver testDriver;
+
+
+    @Override
+    public void activate() {
+        mepStore = createMock(MepStore.class);
+        cfmMdService = new MockCfmMdService();
+        deviceService = createMock(DeviceService.class);
+        ((CfmMdManager) cfmMdService).activate();
+
+        device1 = new DefaultDevice(
+                ProviderId.NONE, DEVICE_ID1, Device.Type.SWITCH,
+                TEST_MFR, TEST_HW_VERSION, TEST_SW_VERSION, TEST_SN,
+                new ChassisId(1),
+                DefaultAnnotations.builder().set(AnnotationKeys.DRIVER, TEST_DRIVER).build());
+
+        Map<Class<? extends Behaviour>, Class<? extends Behaviour>> behaviours = new HashMap<>();
+        behaviours.put(DeviceDescriptionDiscovery.class, TestDeviceDiscoveryBehavior.class);
+        behaviours.put(CfmMepProgrammable.class, TestCfmMepProgrammable.class);
+        behaviours.put(SoamDmProgrammable.class, TestSoamDmProgrammable.class);
+
+        TestUtils.setField(this, "coreService", new TestCoreService());
+        TestUtils.setField(this, "deviceService", deviceService);
+        injectEventDispatcher(this, new TestEventDispatcher());
+
+        testDriver = new DefaultDriver(
+                TEST_DRIVER, new ArrayList<Driver>(),
+                TEST_MFR, TEST_HW_VERSION, TEST_SW_VERSION,
+                behaviours, new HashMap<>());
+
+        try {
+            Mep mep1 = DefaultMep.builder(
+                        MepId.valueOf((short) 10),
+                        DEVICE_ID1,
+                        PortNumber.P0,
+                        Mep.MepDirection.UP_MEP,
+                        MdIdCharStr.asMdId("md-1"),
+                        MaIdCharStr.asMaId("ma-1-1"))
+                    .build();
+
+            expect(mepStore.getMep(new MepKeyId(mep1))).andReturn(Optional.of(mep1)).anyTimes();
+        } catch (CfmConfigException e) {
+            throw new IllegalArgumentException("Error creating MEPs for test", e);
+        }
+    }
+
+    private class TestCoreService extends CoreServiceAdapter {
+
+        @Override
+        public IdGenerator getIdGenerator(String topic) {
+            return new IdGenerator() {
+                private AtomicLong counter = new AtomicLong(0);
+
+                @Override
+                public long getNewId() {
+                    return counter.getAndIncrement();
+                }
+            };
+        }
+    }
+}
diff --git a/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/yang/MockNetconfSessionEa1000.java b/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/yang/MockNetconfSessionEa1000.java
index b9d4342..6bf67c0 100644
--- a/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/yang/MockNetconfSessionEa1000.java
+++ b/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/yang/MockNetconfSessionEa1000.java
@@ -478,11 +478,12 @@
                     + "(<config xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">)\\R?"
                     + "(<mef-cfm).*"
                     + "(<maintenance-domain>)\\R?"
-                    + "(<id/>)?\\R?"
-                    + "(<name>)([a-zA-Z0-9\\-:\\.]){1,48}(</name>)\\R?"
+                    + "(<id>[0-9]{1,5}</id>)?\\R?"
+                    + "((<name>)([a-zA-Z0-9\\-:\\.]){1,48}(</name>))?\\R?"
                     + "(<maintenance-association>)\\R?"
-                    + "(<id/>)?\\R?"
-                    + "(<name>)([a-zA-Z0-9\\-:\\.]){1,48}(</name>)\\R?"
+                    + "(<id>[0-9]{1,5}</id>)?\\R?"
+                    + "((<name>[a-zA-Z0-9\\-:\\.]{1,48}</name>)|"
+                    + "(<name-primary-vid>[0-9]{1,4}</name-primary-vid>))?\\R?"
                     + "(<maintenance-association-end-point nc:operation=\"delete\">)\\R?"
                     + "(<mep-identifier>)[0-9]{1,4}(</mep-identifier>)\\R?"
                     + "(</maintenance-association-end-point>)\\R?"
@@ -492,6 +493,85 @@
                     + "(</config>)\\R?"
                     + "(</edit-config>)\\R?(</rpc>)\\R?(]]>){2}", Pattern.DOTALL);
 
+    //For testGetConfigMseaCfmEssentials
+    private Pattern sampleXmlRegexCreateMseaCfmMa =
+            Pattern.compile("(<\\?xml).*(<rpc).*(<edit-config>)\\R?"
+                    + "(<target>\\R?<running/>\\R?</target>)\\R?"
+                    + "(<config xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">)\\R?"
+                    + "(<mef-cfm).*"
+                    + "(<maintenance-domain>)\\R?"
+                    + "(<id>)([0-9]){1,4}(</id>)\\R?"
+                    + "((<md-level>)([0-9]){1}(</md-level>))?\\R?"
+                    + "((<name>)([a-zA-Z0-9\\-:\\.]){1,48}(</name>))?\\R?"
+                    + "((<maintenance-association>)\\R?"
+                    + "(<id>)([0-9]){1,4}(</id>)\\R?"
+                    + "((<ccm-interval>)(3.3ms)(</ccm-interval>))?\\R?"
+                    + "((<remote-meps>)([0-9]){1,4}(</remote-meps>))*\\R?"
+                    + "(((<name>)([a-zA-Z0-9\\-:\\.]){1,48}(</name>))|"
+                    + "((<name-primary-vid>)([0-9]){1,4}(</name-primary-vid>)))?\\R?"
+                    + "((<component-list>)\\R?"
+                    + "(<vid>)([0-9]){1,4}(</vid>)\\R?"
+                    + "(</component-list>))?\\R?"
+                    + "(</maintenance-association>))*\\R?"
+                    + "(</maintenance-domain>)\\R?"
+                    + "(</mef-cfm>)\\R?"
+                    + "(</config>)\\R?"
+                    + "(</edit-config>)\\R?(</rpc>)\\R?(]]>){2}", Pattern.DOTALL);
+
+    //For testGetConfigMseaCfmEssentials
+    private Pattern sampleXmlRegexDeleteMseaCfmMa =
+            Pattern.compile("(<\\?xml).*(<rpc).*(<edit-config>)\\R?"
+                    + "(<target>\\R?<running/>\\R?</target>)\\R?"
+                    + "(<config xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">)\\R?"
+                    + "(<mef-cfm).*"
+                    + "(<maintenance-domain>)\\R?"
+                    + "((<id/>)|((<id>)([0-9]){1,4}(</id>)))?\\R?"
+                    + "((<name>)([a-zA-Z0-9\\-:\\.]){1,48}(</name>))?\\R?"
+                    + "(<maintenance-association nc:operation=\"delete\">)\\R?"
+                    + "((<id/>)|((<id>)([0-9]){1,4}(</id>)))?\\R?"
+                    + "(((<name>)([a-zA-Z0-9\\-:\\.]){1,48}(</name>))|"
+                    + "((<name-primary-vid>)([0-9]){1,4}(</name-primary-vid>)))?\\R?"
+                    + "(</maintenance-association>)\\R?"
+                    + "(</maintenance-domain>)\\R?"
+                    + "(</mef-cfm>)\\R?"
+                    + "(</config>)\\R?"
+                    + "(</edit-config>)\\R?(</rpc>)\\R?(]]>){2}", Pattern.DOTALL);
+
+    //For testDeleteMseaMepRemoteMep
+    private Pattern sampleXmlRegexDeleteMseaCfmRmep =
+            Pattern.compile("(<\\?xml).*(<rpc).*(<edit-config>)\\R?"
+                    + "(<target>\\R?<running/>\\R?</target>)\\R?"
+                    + "(<config xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">)\\R?"
+                    + "(<mef-cfm).*"
+                    + "(<maintenance-domain>)\\R?"
+                    + "((<id>)[0-9]{1,4}(</id>))?\\R?"
+                    + "((<name>)([a-zA-Z0-9\\-:\\.]){1,48}(</name>))?\\R?"
+                    + "(<maintenance-association>)\\R?"
+                    + "((<id>)[0-9]{1,4}(</id>))?\\R?"
+                    + "((<remote-meps nc:operation=\"delete\">)([0-9]){1,4}(</remote-meps>))*\\R?"
+                    + "(((<name>)([a-zA-Z0-9\\-:\\.]){1,48}(</name>))|"
+                    + "((<name-primary-vid>)([0-9]){1,4}(</name-primary-vid>)))?\\R?"
+                    + "(</maintenance-association>)\\R?"
+                    + "(</maintenance-domain>)\\R?"
+                    + "(</mef-cfm>)\\R?"
+                    + "(</config>)\\R?"
+                    + "(</edit-config>)\\R?(</rpc>)\\R?(]]>){2}", Pattern.DOTALL);
+
+    //For testDeleteMseaMd
+    private Pattern sampleXmlRegexDeleteMseaCfmMd =
+            Pattern.compile("(<\\?xml).*(<rpc).*(<edit-config>)\\R?"
+                    + "(<target>\\R?<running/>\\R?</target>)\\R?"
+                    + "(<config xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">)\\R?"
+                    + "(<mef-cfm).*"
+                    + "(<maintenance-domain nc:operation=\"delete\">)\\R?"
+                    + "((<id/>)|(<id>([0-9]){1,4}(</id>)))?\\R?"
+                    + "(((<name>)([a-zA-Z0-9\\-:\\.]){1,48}(</name>))|\\R?"
+                    + "((<name-domain-name>)([a-zA-Z0-9\\-\\.]){1,48}(</name-domain-name>)))?\\R?"
+                    + "(</maintenance-domain>)\\R?"
+                    + "(</mef-cfm>)\\R?"
+                    + "(</config>)\\R?"
+                    + "(</edit-config>)\\R?(</rpc>)\\R?(]]>){2}", Pattern.DOTALL);
+
 
     private Pattern sampleXmlRegexTransmitLoopback =
             Pattern.compile("(<\\?xml).*(<rpc).*\\R?"
@@ -1310,6 +1390,18 @@
         } else if (sampleXmlRegexDeleteMseaCfmMep.matcher(request).matches()) {
             return SAMPLE_REPLY_OK;
 
+        } else if (sampleXmlRegexCreateMseaCfmMa.matcher(request).matches()) {
+            return SAMPLE_REPLY_OK;
+
+        } else if (sampleXmlRegexDeleteMseaCfmMa.matcher(request).matches()) {
+            return SAMPLE_REPLY_OK;
+
+        } else if (sampleXmlRegexDeleteMseaCfmRmep.matcher(request).matches()) {
+            return SAMPLE_REPLY_OK;
+
+        } else if (sampleXmlRegexDeleteMseaCfmMd.matcher(request).matches()) {
+            return SAMPLE_REPLY_OK;
+
         } else if (sampleXmlRegexGetMseaDelay.matcher(request).matches()) {
             return SAMPLE_MSEACFM_DELAY_MEASUREMENT_FULL_REPLY;
 
diff --git a/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/yang/MseaCfmManagerTest.java b/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/yang/MseaCfmManagerTest.java
index fe99bf3..bb65a4d 100644
--- a/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/yang/MseaCfmManagerTest.java
+++ b/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/yang/MseaCfmManagerTest.java
@@ -36,20 +36,31 @@
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
 import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
 import org.onosproject.incubator.net.l2monitoring.soam.SoamId;
+import org.onosproject.netconf.DatastoreId;
 import org.onosproject.netconf.NetconfDeviceInfo;
 import org.onosproject.netconf.NetconfException;
 import org.onosproject.netconf.NetconfSession;
 import org.onosproject.yang.gen.v1.ietfyangtypes.rev20130715.ietfyangtypes.MacAddress;
 import org.onosproject.yang.gen.v1.mseacfm.rev20160229.MseaCfm;
+import org.onosproject.yang.gen.v1.mseacfm.rev20160229.MseaCfmOpParam;
 import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.DefaultMefCfm;
 import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.MefCfm;
 import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.abortloopback.AbortLoopbackInput;
 import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.abortloopback.DefaultAbortLoopbackInput;
 import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.DefaultMaintenanceDomain;
 import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.MaintenanceDomain;
+import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.DefaultMaintenanceAssociation;
+import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.MaintenanceAssociation;
+import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.maintenanceassociation.DefaultMaintenanceAssociationEndPoint;
 import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.maintenanceassociation.MaintenanceAssociationEndPoint;
+import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.maintenanceassociation.manameandtypecombo.DefaultNamePrimaryVid;
+import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.maintenanceassociation.manameandtypecombo.NamePrimaryVid;
+import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.maintenanceassociation.manameandtypecombo.nameprimaryvid.NamePrimaryVidUnion;
 import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.mdnameandtypecombo.DefaultNameCharacterString;
+import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.mdnameandtypecombo.DefaultNameDomainName;
 import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.mdnameandtypecombo.NameCharacterString;
+import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.mdnameandtypecombo.NameDomainName;
+import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.mdnameandtypecombo.namedomainname.NameDomainNameUnion;
 import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.targetaddressgroup.AddressType;
 import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.targetaddressgroup.addresstype.DefaultMacAddress;
 import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.targetaddressgroup.addresstype.DefaultMepId;
@@ -194,10 +205,173 @@
 //        mseaCfmService.setMseaCfm(mseaCfmOpParam, session, NcDsType.running);
     }
 
+    /**
+     * Using mep Id 10.
+     */
     @Test
+    public void testDeleteMseaMep() {
+        MaintenanceAssociationEndPoint mep10 = new DefaultMaintenanceAssociationEndPoint();
+        mep10.mepIdentifier(MepIdType.of(10));
+
+        MaintenanceAssociation ma1100 = new DefaultMaintenanceAssociation();
+        NamePrimaryVid pvid1100Name = new DefaultNamePrimaryVid();
+        pvid1100Name.namePrimaryVid(NamePrimaryVidUnion.fromString("1100"));
+        ma1100.maNameAndTypeCombo(pvid1100Name);
+        ma1100.id((short) 1100);
+        ma1100.addToMaintenanceAssociationEndPoint(mep10);
+
+        MaintenanceDomain md = new DefaultMaintenanceDomain();
+        NameCharacterString mdName = new DefaultNameCharacterString();
+        mdName.name(new Identifier45("md-1"));
+        md.mdNameAndTypeCombo(mdName);
+        md.id((short) 1);
+        md.addToMaintenanceAssociation(ma1100);
+
+        MefCfm mefCfm = new DefaultMefCfm();
+        mefCfm.addToMaintenanceDomain(md);
+        MseaCfmOpParam mseaCfm = new MseaCfmOpParam();
+        mseaCfm.mefCfm(mefCfm);
+
+        try {
+            boolean deleted = mseaCfmService.deleteMseaMep(mseaCfm, session, DatastoreId.RUNNING);
+            assertTrue(deleted);
+        } catch (NetconfException e) {
+            e.printStackTrace();
+            fail();
+        } catch (CfmConfigException e) {
+            e.printStackTrace();
+            fail();
+        }
+    }
+
+    /**
+     * Using mep Id 10.
+     */
+    @Test
+    public void testDeleteMseaMa() {
+        MaintenanceAssociation ma1300 = new DefaultMaintenanceAssociation();
+        NamePrimaryVid pvid1300Name = new DefaultNamePrimaryVid();
+        pvid1300Name.namePrimaryVid(NamePrimaryVidUnion.fromString("1300"));
+        ma1300.id((short) 1300);
+        ma1300.maNameAndTypeCombo(pvid1300Name);
+
+        MaintenanceDomain md = new DefaultMaintenanceDomain();
+        NameCharacterString mdName = new DefaultNameCharacterString();
+        mdName.name(new Identifier45("md-13"));
+        md.mdNameAndTypeCombo(mdName);
+        md.id((short) 13);
+        md.addToMaintenanceAssociation(ma1300);
+
+        MefCfm mefCfm = new DefaultMefCfm();
+        mefCfm.addToMaintenanceDomain(md);
+        MseaCfmOpParam mseaCfm = new MseaCfmOpParam();
+        mseaCfm.mefCfm(mefCfm);
+
+        try {
+            boolean deleted = mseaCfmService.deleteMseaMa(mseaCfm, session, DatastoreId.RUNNING);
+            assertTrue(deleted);
+        } catch (NetconfException e) {
+            e.printStackTrace();
+            fail();
+        } catch (CfmConfigException e) {
+            e.printStackTrace();
+            fail();
+        }
+    }
+
+
+    @Test
+    public void testDeleteMseaRemoteMep() {
+        MaintenanceAssociation ma1100 = new DefaultMaintenanceAssociation();
+        NamePrimaryVid pvid1100Name = new DefaultNamePrimaryVid();
+        pvid1100Name.namePrimaryVid(NamePrimaryVidUnion.fromString("1100"));
+        ma1100.maNameAndTypeCombo(pvid1100Name);
+        ma1100.id((short) 1100);
+        ma1100.addToRemoteMeps(MepIdType.of(100));
+        ma1100.addToRemoteMeps(MepIdType.of(101));
+
+        MaintenanceDomain md = new DefaultMaintenanceDomain();
+        NameCharacterString mdName = new DefaultNameCharacterString();
+        mdName.name(new Identifier45("md-1"));
+        md.mdNameAndTypeCombo(mdName);
+        md.id((short) 1);
+        md.addToMaintenanceAssociation(ma1100);
+
+        MefCfm mefCfm = new DefaultMefCfm();
+        mefCfm.addToMaintenanceDomain(md);
+        MseaCfmOpParam mseaCfm = new MseaCfmOpParam();
+        mseaCfm.mefCfm(mefCfm);
+
+        try {
+            boolean deleted = mseaCfmService.deleteMseaMaRMep(mseaCfm, session, DatastoreId.RUNNING);
+            assertTrue(deleted);
+        } catch (NetconfException e) {
+            e.printStackTrace();
+            fail();
+        } catch (CfmConfigException e) {
+            e.printStackTrace();
+            fail();
+        }
+    }
+
+    /**
+     * Using mep Id 10.
+     */
+    @Test
+    public void testDeleteMseaMdById() {
+
+        MaintenanceDomain md = new DefaultMaintenanceDomain();
+        NameDomainName mdName = new DefaultNameDomainName();
+        mdName.nameDomainName(NameDomainNameUnion.fromString("www.opennetworking.org"));
+        md.mdNameAndTypeCombo(mdName);
+        md.id((short) 10);
+
+        MefCfm mefCfm = new DefaultMefCfm();
+        mefCfm.addToMaintenanceDomain(md);
+        MseaCfmOpParam mseaCfm = new MseaCfmOpParam();
+        mseaCfm.mefCfm(mefCfm);
+
+        try {
+            boolean deleted = mseaCfmService.deleteMseaMd(mseaCfm, session, DatastoreId.RUNNING);
+            assertTrue(deleted);
+        } catch (NetconfException e) {
+            e.printStackTrace();
+            fail();
+        } catch (CfmConfigException e) {
+            e.printStackTrace();
+            fail();
+        }
+    }
+
+    /**
+     * Using mep Id 10.
+     */
+    @Test
+    public void testDeleteMseaMdByName() {
+
+        MaintenanceDomain md = new DefaultMaintenanceDomain();
+        NameDomainName mdName = new DefaultNameDomainName();
+        mdName.nameDomainName(NameDomainNameUnion.fromString("www.opennetworking.org"));
+        md.mdNameAndTypeCombo(mdName);
+
+        MefCfm mefCfm = new DefaultMefCfm();
+        mefCfm.addToMaintenanceDomain(md);
+        MseaCfmOpParam mseaCfm = new MseaCfmOpParam();
+        mseaCfm.mefCfm(mefCfm);
+
+        try {
+            mseaCfmService.deleteMseaMd(mseaCfm, session, DatastoreId.RUNNING);
+            fail("Should not have succeeded as no numeric id was given");
+        } catch (NetconfException | CfmConfigException e) {
+            assertEquals("An MD numeric ID must be given", e.getMessage());
+        }
+    }
+
+
     /**
      * Using Remote remote MEP ID and all arguments.
      */
+    @Test
     public void testTransmitLoopback1() {
         TransmitLoopbackInput lbTr1 = new DefaultTransmitLoopbackInput();
         lbTr1.maintenanceDomain(Short.valueOf((short) 1));
@@ -221,10 +395,10 @@
         }
     }
 
-    @Test
     /**
      * Using Remote Mac address in place of remote MEP ID and fewer arguments.
      */
+    @Test
     public void testTransmitLoopback2() {
         TransmitLoopbackInput lbTr2 = new DefaultTransmitLoopbackInput();
 
diff --git a/drivers/netconf/src/main/java/org/onosproject/drivers/netconf/NetconfControllerConfig.java b/drivers/netconf/src/main/java/org/onosproject/drivers/netconf/NetconfControllerConfig.java
index 70f01a6..c6a4134 100644
--- a/drivers/netconf/src/main/java/org/onosproject/drivers/netconf/NetconfControllerConfig.java
+++ b/drivers/netconf/src/main/java/org/onosproject/drivers/netconf/NetconfControllerConfig.java
@@ -96,6 +96,7 @@
                     );
                 } catch (NetconfException e) {
                     log.error("Cannot comunicate to device {} , exception {}", deviceId, e.getMessage());
+                    return;
                 }
                 device.getSession().editConfig(config.substring(config.indexOf("-->") + 3));
             } catch (NullPointerException e) {
diff --git a/drivers/optical/src/main/resources/optical-drivers.xml b/drivers/optical/src/main/resources/optical-drivers.xml
index 743cbd5..25be1a3 100644
--- a/drivers/optical/src/main/resources/optical-drivers.xml
+++ b/drivers/optical/src/main/resources/optical-drivers.xml
@@ -102,6 +102,7 @@
         <!-- TODO use OF1.4 reported information when ready -->
         <behaviour api="org.onosproject.net.behaviour.LambdaQuery"
                    impl="org.onosproject.driver.optical.query.ConfigLambdaQuery"/>
+        <property name="meterCapable">false</property>
     </driver>
 
     <driver name="optical-config" manufacturer="config" hwVersion="config" swVersion="config">
diff --git a/drivers/optical/src/test/java/org/onosproject/driver/optical/config/FlowTableConfigTest.java b/drivers/optical/src/test/java/org/onosproject/driver/optical/config/FlowTableConfigTest.java
index b6e6d95..0dbdbcc 100644
--- a/drivers/optical/src/test/java/org/onosproject/driver/optical/config/FlowTableConfigTest.java
+++ b/drivers/optical/src/test/java/org/onosproject/driver/optical/config/FlowTableConfigTest.java
@@ -51,11 +51,11 @@
     private static final DeviceId DID = DeviceId.deviceId("of:0000000000000001");
     private static final PortNumber PN_1 = portNumber(1);
     private static final PortNumber PN_2 = portNumber(2);
-    private static final int FLOW_ID_3 = 3;
+    private static final long FLOW_ID_3 = 3;
     private static final int PRIO_4 = 4;
 
     private static final DefaultApplicationId APP_ID =
-                new DefaultApplicationId(FLOW_ID_3 >>> 48, "test");
+                new DefaultApplicationId((int) (FLOW_ID_3 >>> 48), "test");
 
     private static final OchSignal LAMBDA_42 = OchSignal.newFlexGridSlot(42);
 
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimePacketProgrammable.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimePacketProgrammable.java
index e21dd19..6c61148 100644
--- a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimePacketProgrammable.java
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimePacketProgrammable.java
@@ -37,7 +37,7 @@
 
         final PiPipelineInterpreter interpreter = device.is(PiPipelineInterpreter.class)
                 ? device.as(PiPipelineInterpreter.class) : null;
-        if (!device.is(PiPipelineInterpreter.class)) {
+        if (interpreter == null) {
             log.warn("Device {} with pipeconf {} has no interpreter, aborting emit operation", deviceId, pipeconf.id());
             return;
         }
diff --git a/drivers/utilities/src/main/java/org/onosproject/drivers/utilities/YangXmlUtils.java b/drivers/utilities/src/main/java/org/onosproject/drivers/utilities/YangXmlUtils.java
index bb4dfa5..08ef181 100644
--- a/drivers/utilities/src/main/java/org/onosproject/drivers/utilities/YangXmlUtils.java
+++ b/drivers/utilities/src/main/java/org/onosproject/drivers/utilities/YangXmlUtils.java
@@ -195,7 +195,7 @@
      *
      * @return instance of YangXmlUtils
      */
-    public static YangXmlUtils getInstance() {
+    public static synchronized YangXmlUtils getInstance() {
         if (instance == null) {
             instance = new YangXmlUtils();
         }
diff --git a/features/features.xml b/features/features.xml
index d3f395e..567eda2 100644
--- a/features/features.xml
+++ b/features/features.xml
@@ -59,7 +59,7 @@
         <bundle>mvn:com.typesafe/config/1.2.1</bundle>
         <bundle>mvn:com.googlecode.concurrent-trees/concurrent-trees/2.6.0</bundle>
         <bundle>mvn:commons-io/commons-io/2.4</bundle>
-        <bundle>mvn:io.atomix/atomix/2.0.12</bundle>
+        <bundle>mvn:io.atomix/atomix/2.0.14</bundle>
 
         <bundle>mvn:org.glassfish.jersey.core/jersey-client/2.25.1</bundle>
 
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/faultmanagement/alarm/DefaultAlarm.java b/incubator/api/src/main/java/org/onosproject/incubator/net/faultmanagement/alarm/DefaultAlarm.java
index 1422dde..fd4bad0 100644
--- a/incubator/api/src/main/java/org/onosproject/incubator/net/faultmanagement/alarm/DefaultAlarm.java
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/faultmanagement/alarm/DefaultAlarm.java
@@ -427,8 +427,6 @@
             checkNotNull(id, "Must specify an alarm id");
             checkNotNull(deviceId, "Must specify a device");
             checkNotNull(description, "Must specify a description");
-            checkNotNull(timeRaised, "Must specify a time raised");
-            checkNotNull(timeUpdated, "Must specify a time updated");
             checkNotNull(severity, "Must specify a severity");
 
             return new DefaultAlarm(id, deviceId, description, source, timeRaised, timeUpdated, timeCleared,
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/DefaultMaintenanceAssociation.java b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/DefaultMaintenanceAssociation.java
index 5d827f6..38c3d6f 100644
--- a/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/DefaultMaintenanceAssociation.java
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/DefaultMaintenanceAssociation.java
@@ -213,6 +213,12 @@
         }
 
         @Override
+        public MaBuilder removeFromRemoteMepIdList(MepId remoteMep) {
+            this.remoteMepIdList.remove(remoteMep);
+            return this;
+        }
+
+        @Override
         public MaBuilder ccmInterval(CcmInterval ccmInterval) {
             this.ccmInterval = ccmInterval;
             return this;
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/MaintenanceAssociation.java b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/MaintenanceAssociation.java
index f6e1f3c..37c1a84 100644
--- a/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/MaintenanceAssociation.java
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/MaintenanceAssociation.java
@@ -113,6 +113,8 @@
 
         MaBuilder addToRemoteMepIdList(MepId remoteMep);
 
+        MaBuilder removeFromRemoteMepIdList(MepId remoteMep);
+
         MaBuilder addToComponentList(Component component);
 
         MaBuilder maNumericId(short maNumericId);
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/identifier/MepKeyId.java b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/identifier/MepKeyId.java
new file mode 100644
index 0000000..afb337b
--- /dev/null
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/identifier/MepKeyId.java
@@ -0,0 +1,87 @@
+/*
+ * 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.incubator.net.l2monitoring.cfm.identifier;
+
+import org.onosproject.incubator.net.l2monitoring.cfm.Mep;
+
+/**
+ * Immutable class to represent a unique identifier of a Mep.
+ */
+public class MepKeyId {
+    private MdId mdId;
+    private MaIdShort maId;
+    private MepId mepId;
+
+    public MepKeyId(MdId mdId, MaIdShort maId, MepId mepId) {
+        this.mdId = mdId;
+        this.maId = maId;
+        this.mepId = mepId;
+        if (mdId == null || maId == null || mepId == null) {
+            throw new IllegalArgumentException("Arguments to MepKeyId constructor cannot be null");
+        }
+    }
+
+    public MepKeyId(Mep mep) {
+        this.mdId = mep.mdId();
+        this.maId = mep.maId();
+        this.mepId = mep.mepId();
+    }
+
+    public MdId mdId() {
+        return mdId;
+    }
+
+    public MaIdShort maId() {
+        return maId;
+    }
+
+    public MepId mepId() {
+        return mepId;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        MepKeyId mepKeyId = (MepKeyId) o;
+
+        if (mdId != null ? !mdId.equals(mepKeyId.mdId) : mepKeyId.mdId != null) {
+            return false;
+        }
+        if (maId != null ? !maId.equals(mepKeyId.maId) : mepKeyId.maId != null) {
+            return false;
+        }
+        return mepId != null ? mepId.equals(mepKeyId.mepId) : mepKeyId.mepId == null;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = mdId != null ? mdId.hashCode() : 0;
+        result = 31 * result + (maId != null ? maId.hashCode() : 0);
+        result = 31 * result + (mepId != null ? mepId.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return mdId.mdName() + "/" + maId.maName() + "/" + mepId();
+    }
+}
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMdService.java b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMdService.java
index 9c9aa46..b495eb9 100644
--- a/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMdService.java
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMdService.java
@@ -18,6 +18,7 @@
 import java.util.Collection;
 import java.util.Optional;
 
+import org.onosproject.event.ListenerService;
 import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceAssociation;
 import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceDomain;
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort;
@@ -26,7 +27,7 @@
 /**
  * For the management of Maintenance Domains and Maintenance Associations.
  */
-public interface CfmMdService {
+public interface CfmMdService extends ListenerService<MdEvent, MdListener> {
 
     /**
      * Get a list of all of the Maintenance Domains on the system.
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMepEvent.java b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMepEvent.java
index 26abc45..cb96b52 100644
--- a/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMepEvent.java
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMepEvent.java
@@ -16,12 +16,12 @@
 package org.onosproject.incubator.net.l2monitoring.cfm.service;
 
 import org.onosproject.event.AbstractEvent;
-import org.onosproject.incubator.net.l2monitoring.cfm.Mep;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepKeyId;
 
 /**
  * Event related to the maintenance of CFM MEPs.
  */
-public class CfmMepEvent extends AbstractEvent<CfmMepEvent.Type, Mep> {
+public class CfmMepEvent extends AbstractEvent<CfmMepEvent.Type, MepKeyId> {
 
     /**
      * Type of Mep events.
@@ -52,10 +52,9 @@
      * Creates an event of a given type and for the specified Mep and the current time.
      *
      * @param type Mep event type
-     * @param mep event Mep subject
+     * @param mepKeyId event Mep subject
      */
-    protected CfmMepEvent(Type type, Mep mep) {
-        super(type, mep);
-        // TODO Auto-generated constructor stub
+    public CfmMepEvent(Type type, MepKeyId mepKeyId) {
+        super(type, mepKeyId);
     }
 }
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMepProgrammable.java b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMepProgrammable.java
index b92777b..1b902cb 100644
--- a/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMepProgrammable.java
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMepProgrammable.java
@@ -15,14 +15,95 @@
  */
 package org.onosproject.incubator.net.l2monitoring.cfm.service;
 
+import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceDomain;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
 import org.onosproject.net.driver.HandlerBehaviour;
 
+import java.util.Optional;
+
 /**
  * Behaviour that allows Layer 2 Monitoring as defined in IEEE 802.1Q be implemented by devices.
  *
  * Has all of the same methods as
- * {@link org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepService} so reuse that
+ * {@link CfmMepServiceBase} so reuse that
  */
-public interface CfmMepProgrammable extends HandlerBehaviour, CfmMepService {
+public interface CfmMepProgrammable extends HandlerBehaviour, CfmMepServiceBase {
+
+
+    /**
+     * Allows an MD and children MAs to be created on a device.
+     * A convenience method that allows an MD to be created on a device
+     * This is a preparation mechanism. If this was not called the MD would be created
+     * at the time of provisioning a MEP anyway
+     * @param mdId The ID of the MD to create - the details will be got from the CfmMdService
+     * @return true when created. false if it already existed
+     * @throws CfmConfigException If the MD already exists
+     */
+    boolean createMdOnDevice(MdId mdId) throws CfmConfigException;
+
+    /**
+     * Allows an MA to be created on an existing MD on a device.
+     * A convenience method that allows an MA to be created
+     * This is a preparation mechanism. If this was not called the MA would be created
+     * at the time of provisioning a MEP anyway. Also MAs can be created when they
+     * are contained in an MD using the createMdOnDevice method
+     * @param mdId The identifier of the MD to create the MA in
+     * @param maId The identifier of the MA to create - the details will be retrieved from the CfmMdService
+     * @return true when created. false if it already existed
+     * @throws CfmConfigException If the MD already exists
+     */
+    boolean createMaOnDevice(MdId mdId, MaIdShort maId) throws CfmConfigException;
+
+    /**
+     * Allows a MD and its MAs to be deleted from a device.
+     * A convenience method that allows an MD that has been provisioned on a
+     * device to be removed.
+     * All Meps must be removed first unless they are Meps unknown to ONOS.
+     * This is a cleanup mechanism. Deleting Meps in the normal way does not celan
+     * up MDs and MAs
+     * @param mdId The identifier of the MD
+     * @param oldMd The MaintenanceDomain that is being deleted
+     * @return true when deleted. false if it did not exist
+     * @throws CfmConfigException If the MD has any MAs that have MEPs then this will fail
+     */
+    boolean deleteMdOnDevice(MdId mdId, Optional<MaintenanceDomain> oldMd) throws CfmConfigException;
+
+    /**
+     * Allows an MA to be deleted from an MD on a device.
+     * A convenience method that allows an MA that has been provisioned on a
+     * device to be removed.
+     * All Meps must be removed first unless they are Meps unknown to ONOS.
+     * This is a cleanup mechanism. Deleting Meps in the normal way does not celan
+     * up MDs and MAs
+     * @param mdId The identifier of the MD
+     * @param maId The identifier of the MA
+     * @param oldMd The MaintenanceDomain from which the MA is being deleted
+     * @return true when deleted. false if it did not exist
+     * @throws CfmConfigException If the MA has any MEPs then this will fail
+     */
+    boolean deleteMaOnDevice(MdId mdId, MaIdShort maId,
+                             Optional<MaintenanceDomain> oldMd) throws CfmConfigException;
+
+    /**
+     * Creates a Remote Mep entry on an MA.
+     * @param mdId The identifier of the MD
+     * @param maId The identifier of the MA
+     * @param remoteMep The remote Mep Id to remove from the MA
+     * @return true when deleted. false if it did not exist
+     * @throws CfmConfigException If the MA does not exist this will fail
+     */
+    boolean createMaRemoteMepOnDevice(MdId mdId, MaIdShort maId, MepId remoteMep) throws CfmConfigException;
+
+    /**
+     * Deletes a Remote Mep entry on an MA.
+     * @param mdId The identifier of the MD
+     * @param maId The identifier of the MA
+     * @param remoteMep The remote Mep Id to remove from the MA
+     * @return true when deleted. false if it did not exist
+     * @throws CfmConfigException If the MA does not exist this will fail
+     */
+    boolean deleteMaRemoteMepOnDevice(MdId mdId, MaIdShort maId, MepId remoteMep) throws CfmConfigException;
 
 }
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMepService.java b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMepService.java
index c0c2f92..c774c65 100644
--- a/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMepService.java
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMepService.java
@@ -17,13 +17,12 @@
 
 import java.util.Collection;
 
+import org.onosproject.event.ListenerService;
 import org.onosproject.incubator.net.l2monitoring.cfm.Mep;
 import org.onosproject.incubator.net.l2monitoring.cfm.MepEntry;
-import org.onosproject.incubator.net.l2monitoring.cfm.MepLbCreate;
-import org.onosproject.incubator.net.l2monitoring.cfm.MepLtCreate;
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort;
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId;
-import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+import org.onosproject.net.DeviceId;
 
 /**
  * For the management of Maintenance Association Endpoints.
@@ -31,7 +30,8 @@
  * These are dependent on the Maintenance Domain service which maintains the
  * Maintenance Domain and Maintenance Associations
  */
-public interface CfmMepService {
+public interface CfmMepService
+        extends ListenerService<CfmMepEvent, CfmMepListener>, CfmMepServiceBase {
     /**
      * Retrieve all {@link org.onosproject.incubator.net.l2monitoring.cfm.MepEntry}(s) belonging to an MA.
      * @param mdName A Maintenance Domain
@@ -43,67 +43,13 @@
             throws CfmConfigException;
 
     /**
-     * Retrieve a named {@link org.onosproject.incubator.net.l2monitoring.cfm.MepEntry} belonging to an MA.
-     * @param mdName A Maintenance Domain
-     * @param maName A Maintetance Association in the MD
-     * @param mepId A Mep Id
-     * @return A MEP Entry or null if none found
-     * @throws CfmConfigException If there a problem with the MD, MA or MEP
-     */
-    MepEntry getMep(MdId mdName, MaIdShort maName, MepId mepId)
-            throws CfmConfigException;
-
-    /**
-     * Delete a named {@link org.onosproject.incubator.net.l2monitoring.cfm.Mep} belonging to an MA.
-     * @param mdName A Maintenance Domain
-     * @param maName A Maintetance Association in the MD
-     * @param mepId A Mep Id
-     * @return true if the MEP was deleted successfully. false if it was not found
+     * Retrieve all {@link org.onosproject.incubator.net.l2monitoring.cfm.Mep}(s) belonging to an MA.
+     * Note: This just returns the configuration part of the Mep, not the MepEntry
+     * which contains config and state
+     * @param deviceId A device id
+     * @return A collection of MEP Entries
      * @throws CfmConfigException If there a problem with the MD or MA
      */
-    boolean deleteMep(MdId mdName, MaIdShort maName, MepId mepId)
+    Collection<Mep> getAllMepsByDevice(DeviceId deviceId)
             throws CfmConfigException;
-
-    /**
-     * Create a named {@link org.onosproject.incubator.net.l2monitoring.cfm.Mep} on an MA.
-     * @param mdName A Maintenance Domain
-     * @param maName A Maintetance Association in the MD
-     * @param mep A Mep object
-     * @return False if it was created successfully. True if the object already exists.
-     * @throws CfmConfigException If there a problem with the MD, MA or MEP
-     */
-    boolean createMep(MdId mdName, MaIdShort maName, Mep mep)
-            throws CfmConfigException;
-
-    /**
-     * Create a {@link org.onosproject.incubator.net.l2monitoring.cfm.MepLbEntry Loopback} session on the named Mep.
-     * @param mdName A Maintenance Domain
-     * @param maName A Maintetance Association in the MD
-     * @param mepId A Mep Id
-     * @param lbCreate The Loopback session details
-     * @throws CfmConfigException If there a problem with the MD, MA or MEP
-     */
-    void transmitLoopback(MdId mdName, MaIdShort maName, MepId mepId,
-            MepLbCreate lbCreate) throws CfmConfigException;
-
-    /**
-     * Abort a {@link org.onosproject.incubator.net.l2monitoring.cfm.MepLbEntry Loopback} session on the named Mep.
-     * @param mdName A Maintenance Domain
-     * @param maName A Maintetance Association in the MD
-     * @param mepId A Mep Id
-     * @throws CfmConfigException If there a problem with the MD, MA or MEP
-     */
-    void abortLoopback(MdId mdName, MaIdShort maName, MepId mepId)
-            throws CfmConfigException;
-
-    /**
-     * Create a {@link org.onosproject.incubator.net.l2monitoring.cfm.MepLtEntry Linktrace} session on the named Mep.
-     * @param mdName A Maintenance Domain
-     * @param maName A Maintetance Association in the MD
-     * @param mepId A Mep Id
-     * @param ltCreate The Linktrace session details
-     * @throws CfmConfigException If there a problem with the MD, MA or MEP
-     */
-    void transmitLinktrace(MdId mdName, MaIdShort maName, MepId mepId,
-            MepLtCreate ltCreate) throws CfmConfigException;
 }
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMepServiceBase.java b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMepServiceBase.java
new file mode 100644
index 0000000..71e5611
--- /dev/null
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMepServiceBase.java
@@ -0,0 +1,95 @@
+/*
+ * 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.incubator.net.l2monitoring.cfm.service;
+
+import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceDomain;
+import org.onosproject.incubator.net.l2monitoring.cfm.Mep;
+import org.onosproject.incubator.net.l2monitoring.cfm.MepEntry;
+import org.onosproject.incubator.net.l2monitoring.cfm.MepLbCreate;
+import org.onosproject.incubator.net.l2monitoring.cfm.MepLtCreate;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+
+import java.util.Optional;
+
+public interface CfmMepServiceBase {
+    /**
+     * Retrieve a named {@link org.onosproject.incubator.net.l2monitoring.cfm.MepEntry} belonging to an MA.
+     * @param mdName A Maintenance Domain
+     * @param maName A Maintetance Association in the MD
+     * @param mepId A Mep Id
+     * @return A MEP Entry or null if none found
+     * @throws CfmConfigException If there a problem with the MD, MA or MEP
+     */
+    MepEntry getMep(MdId mdName, MaIdShort maName, MepId mepId)
+            throws CfmConfigException;
+
+    /**
+     * Delete a named {@link org.onosproject.incubator.net.l2monitoring.cfm.Mep} belonging to an MA.
+     * @param mdName A Maintenance Domain
+     * @param maName A Maintetance Association in the MD
+     * @param mepId A Mep Id
+     * @param oldMd The MaintenanceDomain from which the MEP is being deleted
+     * @return true if the MEP was deleted successfully. false if it was not found
+     * @throws CfmConfigException If there a problem with the MD or MA
+     */
+    boolean deleteMep(MdId mdName, MaIdShort maName, MepId mepId, Optional<MaintenanceDomain> oldMd)
+            throws CfmConfigException;
+
+    /**
+     * Create a named {@link org.onosproject.incubator.net.l2monitoring.cfm.Mep} on an MA.
+     * @param mdName A Maintenance Domain
+     * @param maName A Maintetance Association in the MD
+     * @param mep A Mep object
+     * @return True if it was created successfully. False if the object already exists.
+     * @throws CfmConfigException If there a problem with the MD, MA or MEP
+     */
+    boolean createMep(MdId mdName, MaIdShort maName, Mep mep)
+            throws CfmConfigException;
+
+    /**
+     * Create a {@link org.onosproject.incubator.net.l2monitoring.cfm.MepLbEntry Loopback} session on the named Mep.
+     * @param mdName A Maintenance Domain
+     * @param maName A Maintetance Association in the MD
+     * @param mepId A Mep Id
+     * @param lbCreate The Loopback session details
+     * @throws CfmConfigException If there a problem with the MD, MA or MEP
+     */
+    void transmitLoopback(MdId mdName, MaIdShort maName, MepId mepId,
+                          MepLbCreate lbCreate) throws CfmConfigException;
+
+    /**
+     * Abort a {@link org.onosproject.incubator.net.l2monitoring.cfm.MepLbEntry Loopback} session on the named Mep.
+     * @param mdName A Maintenance Domain
+     * @param maName A Maintetance Association in the MD
+     * @param mepId A Mep Id
+     * @throws CfmConfigException If there a problem with the MD, MA or MEP
+     */
+    void abortLoopback(MdId mdName, MaIdShort maName, MepId mepId)
+            throws CfmConfigException;
+
+    /**
+     * Create a {@link org.onosproject.incubator.net.l2monitoring.cfm.MepLtEntry Linktrace} session on the named Mep.
+     * @param mdName A Maintenance Domain
+     * @param maName A Maintetance Association in the MD
+     * @param mepId A Mep Id
+     * @param ltCreate The Linktrace session details
+     * @throws CfmConfigException If there a problem with the MD, MA or MEP
+     */
+    void transmitLinktrace(MdId mdName, MaIdShort maName, MepId mepId,
+                           MepLtCreate ltCreate) throws CfmConfigException;
+}
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/MdEvent.java b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/MdEvent.java
index e3090f7..49023d4 100644
--- a/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/MdEvent.java
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/MdEvent.java
@@ -15,24 +15,78 @@
  */
 package org.onosproject.incubator.net.l2monitoring.cfm.service;
 
+import com.google.common.base.MoreObjects;
+import org.onlab.util.Tools;
 import org.onosproject.event.AbstractEvent;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMaintenanceDomain;
+import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceDomain;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort;
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId;
 
+import java.util.Optional;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
 /**
  * Event related to the maintenance of CFM MDs.
  */
 public class MdEvent extends AbstractEvent<MdEvent.Type, MdId> {
 
+    private MaIdShort maId;
+    private MaintenanceDomain oldMd;
     /**
      * MD Event types supported.
      */
     public enum Type {
         MD_ADDED,
         MD_REMOVED,
-        MD_UPDATED
+        MD_UPDATED,
+        MA_ADDED,
+        MA_REMOVED
     }
 
     public MdEvent(Type type, MdId mdId) {
         super(type, mdId);
     }
+
+    /**
+     * Constructor that allows the MD to be held in the event.
+     * This is useful if the MD had been deleted - it will be the only way of
+     * retrieving some of its attributes
+     * @param type The type of the event
+     * @param mdId The ID of the MD
+     * @param md The whole MD
+     * @throws CfmConfigException if there's a problem copying MD
+     */
+    public MdEvent(Type type, MdId mdId, MaintenanceDomain md) throws CfmConfigException {
+        super(type, mdId);
+        this.oldMd = DefaultMaintenanceDomain.builder(md).build();
+    }
+
+    public MdEvent(Type type, MdId mdId, MaintenanceDomain md, MaIdShort maId)
+            throws CfmConfigException {
+        super(type, mdId);
+        this.maId = maId;
+        this.oldMd = DefaultMaintenanceDomain.builder(md).build();
+    }
+
+    public Optional<MaIdShort> maId() {
+        return maId == null ? Optional.empty() : Optional.of(maId);
+    }
+
+    public Optional<MaintenanceDomain> md() {
+        return oldMd == null ? Optional.empty() : Optional.of(oldMd);
+    }
+
+    @Override
+    public String toString() {
+        MoreObjects.ToStringHelper helper = toStringHelper(this)
+                .add("time", Tools.defaultOffsetDataTime(time()))
+                .add("type", type())
+                .add("subject", subject());
+        if (maId != null) {
+            helper = helper.add("subject2", maId);
+        }
+        return helper.toString();
+    }
 }
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/MepStore.java b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/MepStore.java
new file mode 100644
index 0000000..ed4bb35
--- /dev/null
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/MepStore.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2018-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.incubator.net.l2monitoring.cfm.service;
+
+import org.onosproject.incubator.net.l2monitoring.cfm.Mep;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepKeyId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.store.Store;
+
+import java.util.Collection;
+import java.util.Optional;
+
+/**
+ * {@link org.onosproject.incubator.net.l2monitoring.cfm.Mep Maintenance Association Endpoint's} storage interface.
+ * Note: because the Mep is immutable if anything needs to be
+ * changed in it, then it must be replaced in the store.
+ */
+public interface MepStore extends Store<CfmMepEvent, MepStoreDelegate> {
+    /**
+     * Get a list of all of the Meps on the system.
+     * @return A collection Meps from the Mep Store.
+     */
+    Collection<Mep> getAllMeps();
+
+    /**
+     * Get all Meps by MD.
+     * @param mdName An identifier for the Maintenance Domain
+     * @return MEPs from the MEP Store. Empty if not found.
+     */
+    Collection<Mep> getMepsByMd(MdId mdName);
+
+    /**
+     * Get all Meps by MD, MA.
+     * @param mdName An identifier for the Maintenance Domain
+     * @param maName An identifier for the Maintenance Association
+     * @return MEPs from the MEP Store. Empty if not found.
+     */
+    Collection<Mep> getMepsByMdMa(MdId mdName, MaIdShort maName);
+
+    /**
+     * Get all Meps by DeviceId.
+     * @param deviceId An identifier for the Device
+     * @return MEPs from the MEP Store. Empty if not found.
+     */
+    Collection<Mep> getMepsByDeviceId(DeviceId deviceId);
+
+    /**
+     * Get a specific Mep by its Mep key id.
+     * @param mepKeyId An unique identifier for the MEP
+     * @return A MEP from the MEP Store. Empty if not found.
+     */
+    Optional<Mep> getMep(MepKeyId mepKeyId);
+
+    /**
+     * Delete a specific Mep by its identifier.
+     * @param mepKeyId An unique identifier for the MEP
+     * @return True if the Mep was found and deleted
+     */
+    boolean deleteMep(MepKeyId mepKeyId);
+
+    /**
+     * Create or replace a Mep.
+     * @param mepKeyId An unique identifier for the MEP
+     * @param mep The new MEP
+     * @return true if an Mep of this name already existed
+     */
+    boolean createUpdateMep(MepKeyId mepKeyId, Mep mep);
+
+}
diff --git a/apps/restconf/utils/src/main/java/org/onosproject/restconf/utils/exceptions/package-info.java b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/MepStoreDelegate.java
similarity index 68%
copy from apps/restconf/utils/src/main/java/org/onosproject/restconf/utils/exceptions/package-info.java
copy to incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/MepStoreDelegate.java
index a0fe88e..6d5dfd3 100644
--- a/apps/restconf/utils/src/main/java/org/onosproject/restconf/utils/exceptions/package-info.java
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/MepStoreDelegate.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017-present Open Networking Foundation
+ * Copyright 2018-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.
@@ -13,8 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package org.onosproject.incubator.net.l2monitoring.cfm.service;
+
+import org.onosproject.store.StoreDelegate;
 
 /**
- * Parse utils custom exceptions.
+ * Delegate for MEP Store.
  */
-package org.onosproject.restconf.utils.exceptions;
\ No newline at end of file
+public interface MepStoreDelegate extends StoreDelegate<CfmMepEvent> {
+}
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/dpi/impl/DpiStatisticsManager.java b/incubator/net/src/main/java/org/onosproject/incubator/net/dpi/impl/DpiStatisticsManager.java
index 10f82b4..b49c9cb 100644
--- a/incubator/net/src/main/java/org/onosproject/incubator/net/dpi/impl/DpiStatisticsManager.java
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/dpi/impl/DpiStatisticsManager.java
@@ -67,7 +67,7 @@
 @Service
 public class DpiStatisticsManager implements DpiStatisticsManagerService {
 
-    private static ServerSocket serverSocket;
+    private ServerSocket serverSocket;
     private static int port = 11990; // socket server listening port
 
     private final Logger log = getLogger(getClass());
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/CfmMdManager.java b/incubator/net/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/CfmMdManager.java
index 5a125f5..b18d2f6 100644
--- a/incubator/net/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/CfmMdManager.java
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/CfmMdManager.java
@@ -65,10 +65,8 @@
     @Activate
     public void activate() {
         appId = coreService.registerApplication(APP_ID);
-
         eventDispatcher.addSink(MdEvent.class, listenerRegistry);
         store.setDelegate(delegate);
-
         log.info("CFM Service Started");
     }
 
@@ -163,9 +161,8 @@
     private class InternalStoreDelegate implements MdStoreDelegate {
         @Override
         public void notify(MdEvent event) {
-            log.debug("New MD event: {}", event.subject());
+            log.debug("New MD event: {}", event);
             eventDispatcher.post(event);
         }
     }
-
 }
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/CfmMepManager.java b/incubator/net/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/CfmMepManager.java
index 5723466..e58a570 100644
--- a/incubator/net/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/CfmMepManager.java
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/CfmMepManager.java
@@ -15,10 +15,21 @@
  */
 package org.onosproject.incubator.net.l2monitoring.cfm.impl;
 
+import static org.onlab.util.Tools.groupedThreads;
 import static org.slf4j.LoggerFactory.getLogger;
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
 
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -29,6 +40,8 @@
 import org.onosproject.core.CoreService;
 import org.onosproject.core.IdGenerator;
 import org.onosproject.event.AbstractListenerManager;
+import org.onosproject.event.Event;
+import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceDomain;
 import org.onosproject.incubator.net.l2monitoring.cfm.Mep;
 import org.onosproject.incubator.net.l2monitoring.cfm.MepEntry;
 import org.onosproject.incubator.net.l2monitoring.cfm.MepLbCreate;
@@ -36,12 +49,17 @@
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort;
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId;
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepKeyId;
 import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
 import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMdService;
 import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepEvent;
 import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepListener;
 import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepProgrammable;
 import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepService;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.MdEvent;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.MdListener;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.MepStore;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.MepStoreDelegate;
 import org.onosproject.mastership.MastershipService;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
@@ -61,7 +79,8 @@
 
     private final Logger log = getLogger(getClass());
 
-    private final DeviceListener deviceListener = new InternalDeviceListener();
+    private InternalDeviceListener deviceListener = null;
+    private InternalMdListener mdListener = null;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected DeviceService deviceService;
@@ -78,22 +97,39 @@
     private static final int DEFAULT_POLL_FREQUENCY = 30;
     private int fallbackMepPollFrequency = DEFAULT_POLL_FREQUENCY;
 
+    private InternalEventHandler eventHandler = new InternalEventHandler();
+    private static final Object THREAD_SCHED_LOCK = new Object();
+    private static int numOfEventsQueued = 0;
+    private static int numOfEventsExecuted = 0;
+    private static int numOfHandlerExecution = 0;
+    private static int numOfHandlerScheduled = 0;
+
+    private ScheduledExecutorService executorService = Executors
+            .newScheduledThreadPool(1,
+                    groupedThreads("CfmMepManager", "event-%d", log));
+
+    @SuppressWarnings("unused")
+    private static ScheduledFuture<?> eventHandlerFuture = null;
+    @SuppressWarnings("rawtypes")
+    private ConcurrentLinkedQueue<Event> eventQueue = new ConcurrentLinkedQueue<>();
+
+
     private IdGenerator idGenerator;
 
-    //FIXME Get rid of this hack - we will use this in memory to emulate
-    // a store for the short term.
-    //Note: This is not distributed and will not work in a clustered system
-    //TODO Create a MepStore for this
-    private Collection<Mep> mepCollection;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected MepStore mepStore;
 
+    protected final MepStoreDelegate delegate = new InternalStoreDelegate();
 
     @Activate
     public void activate() {
-        //FIXME Get rid of this local list
-        mepCollection = new ArrayList<>();
+        mepStore.setDelegate(delegate);
 
-        eventDispatcher.addSink(CfmMepEvent.class, listenerRegistry);
+        deviceListener = new InternalDeviceListener();
         deviceService.addListener(deviceListener);
+        mdListener = new InternalMdListener();
+        cfmMdService.addListener(mdListener);
+        eventDispatcher.addSink(CfmMepEvent.class, listenerRegistry);
         idGenerator = coreService.getIdGenerator("mep-ids");
         log.info("CFM MEP Manager Started");
     }
@@ -101,9 +137,12 @@
     @Deactivate
     public void deactivate() {
         deviceService.removeListener(deviceListener);
+        cfmMdService.removeListener(mdListener);
         eventDispatcher.removeSink(CfmMepEvent.class);
         log.info("CFM MEP Manager Stopped");
-        mepCollection.clear();
+        mepStore.unsetDelegate(delegate);
+        deviceListener = null;
+        mdListener = null;
     }
 
     @Override
@@ -112,27 +151,28 @@
         //Will throw IllegalArgumentException if ma does not exist
         cfmMdService.getMaintenanceAssociation(mdName, maName);
 
+        Collection<Mep> mepStoreCollection = mepStore.getAllMeps();
         Collection<MepEntry> mepEntryCollection = new ArrayList<>();
 
-        for (Mep mep:mepCollection) {
+        for (Mep mep : mepStoreCollection) {
             if (mep.mdId().equals(mdName) && mep.maId().equals(maName)) {
                 DeviceId mepDeviceId = mep.deviceId();
                 if (deviceService.getDevice(mepDeviceId) == null) {
                     log.warn("Device not found/available " + mepDeviceId +
-                        " for MEP: " + mdName + "/" + maName + "/" + mep.mepId());
+                            " for MEP: " + mdName + "/" + maName + "/" + mep.mepId());
                     continue;
                 } else if (!deviceService.getDevice(mepDeviceId)
-                                        .is(CfmMepProgrammable.class)) {
+                        .is(CfmMepProgrammable.class)) {
                     throw new CfmConfigException("Device " + mepDeviceId +
                             " does not support CfmMepProgrammable behaviour.");
                 }
 
                 log.debug("Retrieving MEP results for Mep {} in MD {}, MA {} "
-                    + "on Device {}", mep.mepId(), mdName, maName, mepDeviceId);
+                        + "on Device {}", mep.mepId(), mdName, maName, mepDeviceId);
                 mepEntryCollection.add(deviceService
-                                        .getDevice(mepDeviceId)
-                                        .as(CfmMepProgrammable.class)
-                                        .getMep(mdName, maName, mep.mepId()));
+                        .getDevice(mepDeviceId)
+                        .as(CfmMepProgrammable.class)
+                        .getMep(mdName, maName, mep.mepId()));
             }
         }
 
@@ -140,75 +180,116 @@
     }
 
     @Override
-    public MepEntry getMep(MdId mdName, MaIdShort maName, MepId mepId) throws CfmConfigException {
-        //Will throw IllegalArgumentException if ma does not exist
-        cfmMdService.getMaintenanceAssociation(mdName, maName);
-
-        for (Mep mep : mepCollection) {
-            if (mep.mdId().equals(mdName) && mep.maId().equals(maName)
-                    && mep.mepId().equals(mepId)) {
-
-                DeviceId mepDeviceId = mep.deviceId();
-                if (deviceService.getDevice(mepDeviceId) == null) {
-                    throw new CfmConfigException("Device not found " + mepDeviceId);
-                } else if (!deviceService.getDevice(mepDeviceId).is(CfmMepProgrammable.class)) {
-                    throw new CfmConfigException("Device " + mepDeviceId +
-                            " does not support CfmMepProgrammable behaviour.");
-                }
-
-                log.debug("Retrieving MEP reults for Mep {} in MD {}, MA {} on Device {}",
-                        mep.mepId(), mdName, maName, mepDeviceId);
-
-                return deviceService.getDevice(mepDeviceId)
-                        .as(CfmMepProgrammable.class).getMep(mdName, maName, mepId);
-            }
-        }
-        return null;
+    public Collection<Mep> getAllMepsByDevice(DeviceId deviceId) throws CfmConfigException {
+        return mepStore.getMepsByDeviceId(deviceId);
     }
 
     @Override
-    public boolean deleteMep(MdId mdName, MaIdShort maName, MepId mepId) throws CfmConfigException {
+    public MepEntry getMep(MdId mdName, MaIdShort maName, MepId mepId) throws CfmConfigException {
+        MepKeyId key = new MepKeyId(mdName, maName, mepId);
+
         //Will throw IllegalArgumentException if ma does not exist
         cfmMdService.getMaintenanceAssociation(mdName, maName);
 
-        for (Mep mep : mepCollection) {
-            if (mep.mdId().equals(mdName) && mep.maId().equals(maName)
-                    && mep.mepId().equals(mepId)) {
-                Device mepDevice = deviceService.getDevice(mep.deviceId());
-                if (mepDevice == null || !mepDevice.is(CfmMepProgrammable.class)) {
-                    throw new CfmConfigException("Unexpeced fault on device drier for "
-                            + mep.deviceId());
-                }
-                boolean deleted = false;
-                try {
-                     deleted = mepDevice.as(CfmMepProgrammable.class)
-                            .deleteMep(mdName, maName, mepId);
-                } catch (CfmConfigException e) {
-                    log.warn("MEP could not be deleted on device - perhaps it "
-                            + "does not exist. Continuing");
-                    mepCollection.remove(mep);
-                    return false;
-                }
-                if (deleted) {
-                    mepCollection.remove(mep);
-                    return true;
-                } else {
-                    return false;
+        Optional<Mep> mepOptional = mepStore.getMep(key);
+        if (mepOptional.isPresent()) {
+            Mep mep = mepOptional.get();
+            DeviceId mepDeviceId = mep.deviceId();
+            if (deviceService.getDevice(mepDeviceId) == null) {
+                throw new CfmConfigException("Device not found " + mepDeviceId);
+            } else if (!deviceService.getDevice(mepDeviceId).is(CfmMepProgrammable.class)) {
+                throw new CfmConfigException("Device " + mepDeviceId +
+                        " does not support CfmMepProgrammable behaviour.");
+            }
+
+            log.debug("Retrieving MEP reults for Mep {} in MD {}, MA {} on Device {}",
+                    mep.mepId(), mdName, maName, mepDeviceId);
+
+            return deviceService.getDevice(mepDeviceId)
+                    .as(CfmMepProgrammable.class).getMep(mdName, maName, mepId);
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public boolean deleteMep(MdId mdName, MaIdShort maName, MepId mepId,
+                             Optional<MaintenanceDomain> oldMd) throws CfmConfigException {
+        MepKeyId key = new MepKeyId(mdName, maName, mepId);
+
+        //Will throw IllegalArgumentException if ma does not exist
+        cfmMdService.getMaintenanceAssociation(mdName, maName);
+
+        //Get the device ID from the MEP
+        Optional<Mep> deletedMep = mepStore.getMep(key);
+        if (!deletedMep.isPresent()) {
+            log.warn("MEP {} not found when deleting Mep", key);
+            return false;
+        }
+
+        DeviceId mepDeviceId = deletedMep.get().deviceId();
+        boolean deleted = mepStore.deleteMep(key);
+
+        Device mepDevice = deviceService.getDevice(mepDeviceId);
+        if (mepDevice == null || !mepDevice.is(CfmMepProgrammable.class)) {
+            throw new CfmConfigException("Unexpeced fault on device driver for "
+                    + mepDeviceId);
+        }
+        try {
+            deleted = mepDevice.as(CfmMepProgrammable.class)
+                    .deleteMep(mdName, maName, mepId, oldMd);
+        } catch (CfmConfigException e) {
+            log.warn("MEP could not be deleted on device - perhaps it "
+                    + "does not exist. Continuing");
+        }
+
+        //Iterate through all other devices and remove as a Remote Mep
+        int mepsOnMdCount = 0;
+        int mepsOnMaCount = 0;
+        for (Mep mep : mepStore.getAllMeps()) {
+            if (mep.deviceId().equals(mepDeviceId) && mdName.equals(mep.mdId())) {
+                mepsOnMdCount++;
+                if (maName.equals(mep.maId())) {
+                    mepsOnMaCount++;
                 }
             }
+            List<DeviceId> alreadyHandledDevices = new ArrayList<>();
+            if (mep.deviceId().equals(mepDeviceId) || !mep.mdId().equals(mdName) ||
+                    !mep.maId().equals(maName) ||
+                    alreadyHandledDevices.contains(mep.deviceId())) {
+                continue;
+            }
+            deviceService.getDevice(mep.deviceId())
+                    .as(CfmMepProgrammable.class)
+                    .deleteMaRemoteMepOnDevice(mdName, maName, mepId);
+            alreadyHandledDevices.add(mep.deviceId());
+            log.info("Deleted RMep entry on {} on device {}",
+                    mdName.mdName() + "/" + maName.maName(), mep.deviceId());
         }
-        return false;
+
+        //Also if this is the last MEP in this MA then delete this MA from device
+        //If this is the last MA in this MD on device, then delete the MD from the device
+        if (mepsOnMdCount == 0) {
+            boolean deletedMd = deviceService.getDevice(mepDeviceId)
+                    .as(CfmMepProgrammable.class).deleteMdOnDevice(mdName, oldMd);
+            log.info("Deleted MD {} from Device {}", mdName.mdName(), mepDeviceId);
+        } else if (mepsOnMaCount == 0) {
+            boolean deletedMa = deviceService.getDevice(mepDeviceId)
+                    .as(CfmMepProgrammable.class).deleteMaOnDevice(mdName, maName, oldMd);
+            log.info("Deleted MA {} from Device {}",
+                    mdName.mdName() + "/" + maName.maName(), mepDeviceId);
+        }
+
+        return deleted;
     }
 
     @Override
     public boolean createMep(MdId mdName, MaIdShort maName, Mep newMep) throws CfmConfigException {
+        MepKeyId key = new MepKeyId(mdName, maName, newMep.mepId());
         log.debug("Creating MEP " + newMep.mepId() + " on MD {}, MA {} on Device {}",
                 mdName, maName, newMep.deviceId().toString());
-        for (Mep mep : mepCollection) {
-            if (mep.mdId().equals(mdName) && mep.maId().equals(maName)
-                    && mep.mepId().equals(newMep.mepId())) {
-                return false;
-            }
+        if (mepStore.getMep(key).isPresent()) {
+            return false;
         }
 
         //Will throw IllegalArgumentException if ma does not exist
@@ -225,7 +306,24 @@
                 deviceService.getDevice(mepDeviceId).as(CfmMepProgrammable.class).createMep(mdName, maName, newMep);
         log.debug("MEP created on {}", mepDeviceId);
         if (deviceResult) {
-            return mepCollection.add(newMep);
+            boolean alreadyExisted = mepStore.createUpdateMep(key, newMep);
+
+            //Add to other Remote Mep List on other devices
+            for (Mep mep:mepStore.getMepsByMdMa(mdName, maName)) {
+                List<DeviceId> alreadyHandledDevices = new ArrayList<>();
+                if (mep.deviceId().equals(mepDeviceId) ||
+                        alreadyHandledDevices.contains(mep.deviceId())) {
+                    continue;
+                }
+                boolean created = deviceService.getDevice(mep.deviceId())
+                        .as(CfmMepProgrammable.class)
+                        .createMaRemoteMepOnDevice(mdName, maName, newMep.mepId());
+                alreadyHandledDevices.add(mep.deviceId());
+                log.info("Created RMep entry on {} on device {}",
+                        mdName.mdName() + "/" + maName.maName(), mep.deviceId());
+            }
+
+            return !alreadyExisted;
         } else {
             return deviceResult;
         }
@@ -233,60 +331,253 @@
 
     @Override
     public void transmitLoopback(MdId mdName, MaIdShort maName,
-            MepId mepId, MepLbCreate lbCreate) throws CfmConfigException {
-        for (Mep mep : mepCollection) {
-            if (mep.mdId().equals(mdName) && mep.maId().equals(maName)
-                    && mep.mepId().equals(mepId)) {
-                log.debug("Transmitting Loopback on MEP {}/{}/{} on Device {}",
-                        mdName, maName, mepId, mep.deviceId());
-                deviceService.getDevice(mep.deviceId())
-                    .as(CfmMepProgrammable.class)
-                    .transmitLoopback(mdName, maName, mepId, lbCreate);
-                return;
-            }
-        }
-        throw new CfmConfigException("Mep " + mdName + "/" + maName + "/"
-                + mepId + " not found when calling Transmit Loopback");
+                                 MepId mepId, MepLbCreate lbCreate) throws CfmConfigException {
+        MepKeyId key = new MepKeyId(mdName, maName, mepId);
+        Mep mep = mepStore.getMep(key)
+                .orElseThrow(() -> new CfmConfigException("Mep " + mdName + "/" + maName + "/"
+                + mepId + " not found when calling Transmit Loopback"));
+
+        log.debug("Transmitting Loopback on MEP {} on Device {}",
+                key, mep.deviceId());
+        deviceService.getDevice(mep.deviceId())
+                .as(CfmMepProgrammable.class)
+                .transmitLoopback(mdName, maName, mepId, lbCreate);
     }
 
     @Override
     public void abortLoopback(MdId mdName, MaIdShort maName, MepId mepId)
             throws CfmConfigException {
-        for (Mep mep : mepCollection) {
-            if (mep.mdId().equals(mdName) && mep.maId().equals(maName)
-                    && mep.mepId().equals(mepId)) {
-                log.debug("Aborting Loopback on MEP {}/{}/{} on Device {}",
-                        mdName, maName, mepId, mep.deviceId());
-                deviceService.getDevice(mep.deviceId())
-                    .as(CfmMepProgrammable.class)
-                    .abortLoopback(mdName, maName, mepId);
-                return;
-            }
-        }
-        throw new CfmConfigException("Mep " + mdName + "/" + maName + "/"
-                + mepId + " not found when calling Transmit Loopback");
+
+        MepKeyId key = new MepKeyId(mdName, maName, mepId);
+        Mep mep = mepStore.getMep(key)
+                .orElseThrow(() -> new CfmConfigException("Mep " + mdName + "/" + maName + "/"
+                        + mepId + " not found when calling Aborting Loopback"));
+
+        log.debug("Aborting Loopback on MEP {} on Device {}",
+                key, mep.deviceId());
+        deviceService.getDevice(mep.deviceId())
+                .as(CfmMepProgrammable.class)
+                .abortLoopback(mdName, maName, mepId);
     }
 
     @Override
     public void transmitLinktrace(MdId mdName, MaIdShort maName, MepId mepId,
-            MepLtCreate ltCreate) {
+                                  MepLtCreate ltCreate) {
         throw new UnsupportedOperationException("Not yet implemented");
     }
 
+    private class InternalMdListener implements MdListener {
+        @Override
+        public boolean isRelevant(MdEvent event) {
+            return event.type().equals(MdEvent.Type.MD_REMOVED) ||
+                    event.type().equals(MdEvent.Type.MA_REMOVED);
+        }
+
+        @Override
+        public void event(MdEvent event) {
+            MdId mdName = event.subject();
+            switch (event.type()) {
+                case MA_REMOVED:
+                case MD_REMOVED:
+                    log.trace("Event {} receieved from MD Service for {}", event.type(), mdName);
+                    scheduleEventHandlerIfNotScheduled(event);
+                    break;
+                default:
+                    log.warn("Unhandled Event {} received from MD Service", event.type());
+                    break;
+            }
+        }
+    }
+
     private class InternalDeviceListener implements DeviceListener {
         @Override
+        public boolean isRelevant(DeviceEvent event) {
+            return event.type().equals(DeviceEvent.Type.DEVICE_REMOVED);
+        }
+
+        @Override
         public void event(DeviceEvent event) {
+            DeviceId deviceId = event.subject().id();
             switch (event.type()) {
+                case DEVICE_ADDED:
+                case PORT_UPDATED:
+                case PORT_ADDED:
+                case DEVICE_UPDATED:
                 case DEVICE_REMOVED:
                 case DEVICE_AVAILABILITY_CHANGED:
-                    DeviceId deviceId = event.subject().id();
-                    if (!deviceService.isAvailable(deviceId)) {
-                        log.warn("Device {} has been removed or changed", deviceId);
-                    }
+                    log.trace("Event {} received from Device Service", event.type());
+                    scheduleEventHandlerIfNotScheduled(event);
                     break;
                 default:
+                    log.warn("Unhandled Event {} received from Device Service", event.type());
                     break;
             }
         }
     }
+
+    @SuppressWarnings("rawtypes")
+    private void scheduleEventHandlerIfNotScheduled(Event event) {
+        synchronized (THREAD_SCHED_LOCK) {
+            eventQueue.add(event);
+            numOfEventsQueued++;
+
+            if ((numOfHandlerScheduled - numOfHandlerExecution) == 0) {
+                //No pending scheduled event handling threads. So start a new one.
+                eventHandlerFuture = executorService
+                        .schedule(eventHandler, 100, TimeUnit.MILLISECONDS);
+                numOfHandlerScheduled++;
+            }
+            log.trace("numOfEventsQueued {}, numOfEventHandlerScheduled {}",
+                    numOfEventsQueued,
+                    numOfHandlerScheduled);
+        }
+    }
+
+    private class InternalEventHandler implements Runnable {
+        @Override
+        public void run() {
+            try {
+                while (true) {
+                    @SuppressWarnings("rawtypes")
+                    Event event;
+                    synchronized (THREAD_SCHED_LOCK) {
+                        if (!eventQueue.isEmpty()) {
+                            event = eventQueue.poll();
+                            numOfEventsExecuted++;
+                        } else {
+                            numOfHandlerExecution++;
+                            log.debug("numOfHandlerExecution {} numOfEventsExecuted {}",
+                                    numOfHandlerExecution, numOfEventsExecuted);
+                            break;
+                        }
+                    }
+                    if (event.type() == DeviceEvent.Type.DEVICE_REMOVED) {
+                        DeviceId deviceId = ((Device) event.subject()).id();
+                        log.info("Processing device removal event for unavailable device {}",
+                                deviceId);
+                        processDeviceRemoved((Device) event.subject());
+                    } else if (event.type() == MdEvent.Type.MD_REMOVED) {
+                        MdId mdName = (MdId) event.subject();
+                        log.info("Processing MD removal event for MD {}",
+                                mdName);
+                        processMdRemoved(mdName, ((MdEvent) event).md().get());
+                    } else if (event.type() == MdEvent.Type.MA_REMOVED) {
+                        MdId mdName = (MdId) event.subject();
+                        MaIdShort maName = ((MdEvent) event).maId().get();
+                        log.info("Processing MA removal event for MA {}",
+                                mdName.mdName() + "/" + maName.maName());
+                        processMaRemoved(mdName, maName, ((MdEvent) event).md().get());
+                    }
+                }
+            } catch (Exception e) {
+                log.error("CfmMepService event handler "
+                        + "thread thrown an exception: {}", e);
+            }
+        }
+    }
+
+    /**
+     * This removes a MEP from the internal list of Meps, and updates remote meps list on other Meps.
+     * Note: This does not call the device's CfmMepProgrammable, because there
+     * would be no point as the device has already been removed from ONOS.
+     * The configuration for this MEP may still be present on the actual device, and
+     * any future config would have to be careful to wipe the Mep from the device
+     * before applying a Mep again
+     * @param removedDevice The device that has been removed
+     */
+    protected void processDeviceRemoved(Device removedDevice) {
+        log.warn("Remove Mep(s) associated with Device: " + removedDevice.id());
+        Collection<Mep> mepListForDevice = mepStore.getMepsByDeviceId(removedDevice.id());
+
+
+        for (Mep mep:mepStore.getAllMeps()) {
+            for (Mep mepForDevice:mepListForDevice) {
+                if (mep.mdId().equals(mepForDevice.mdId()) && mep.maId().equals(mepForDevice.maId())) {
+                    Device mepDevice = deviceService.getDevice(mep.deviceId());
+                    log.info("Removing Remote Mep {} from MA{} on device {}",
+                            mepForDevice.mepId(),
+                            mep.mdId().mdName() + "/" + mep.maId().maName(),
+                            mepDevice.id());
+                    try {
+                        mepDevice.as(CfmMepProgrammable.class)
+                                .deleteMaRemoteMepOnDevice(mep.mdId(), mep.maId(), mepForDevice.mepId());
+                    } catch (CfmConfigException e) {
+                        log.error("Error when removing Remote Mep {} from MA {}. Continuing.",
+                                mep.mdId().mdName() + "/" + mep.maId().maName(),
+                                mepForDevice.mepId());
+                    }
+                }
+            }
+        }
+
+        for (Iterator<Mep> iter = mepListForDevice.iterator(); iter.hasNext();) {
+            mepStore.deleteMep(new MepKeyId(iter.next()));
+        }
+    }
+
+    protected void processMaRemoved(MdId mdId, MaIdShort maId, MaintenanceDomain oldMd) {
+        Set<DeviceId> deviceIdsRemoved = new HashSet<>();
+
+        for (Iterator<Mep> iter = mepStore.getMepsByMdMa(mdId, maId).iterator(); iter.hasNext();) {
+            Mep mepForMdMa = iter.next();
+            DeviceId mepDeviceId = mepForMdMa.deviceId();
+            try {
+                deviceService.getDevice(mepDeviceId).as(CfmMepProgrammable.class)
+                        .deleteMep(mdId, maId, mepForMdMa.mepId(), Optional.of(oldMd));
+                deviceIdsRemoved.add(mepDeviceId);
+            } catch (CfmConfigException e) {
+                log.warn("Could not delete MEP {} from Device {}", mepForMdMa.mepId(), mepDeviceId, e);
+            }
+            iter.remove();
+
+            log.info("Removed MEP {} from Device {} because MA {} was deleted",
+                    mepForMdMa.mepId(), mepDeviceId, mdId.mdName() + "/" + maId.maName());
+        }
+
+        deviceIdsRemoved.forEach(deviceId -> {
+            try {
+                deviceService.getDevice(deviceId).as(CfmMepProgrammable.class)
+                        .deleteMaOnDevice(mdId, maId, Optional.of(oldMd));
+            } catch (CfmConfigException e) {
+                log.warn("Could not delete MA {} from Device {}",
+                        mdId.mdName() + "/" + maId.maName(), deviceId, e);
+            }
+        });
+    }
+
+    protected void processMdRemoved(MdId mdId, MaintenanceDomain oldMd) {
+        Set<DeviceId> deviceIdsRemoved = new HashSet<>();
+        for (Iterator<Mep> iter = mepStore.getMepsByMd(mdId).iterator(); iter.hasNext();) {
+            Mep mep = iter.next();
+            DeviceId mepDeviceId = mep.deviceId();
+            try {
+                deviceService.getDevice(mepDeviceId).as(CfmMepProgrammable.class)
+                        .deleteMep(mdId, mep.maId(), mep.mepId(), Optional.of(oldMd));
+                deviceIdsRemoved.add(mepDeviceId);
+            } catch (CfmConfigException e) {
+                log.warn("Could not delete MEP {} from Device {}", mep.mepId(), mepDeviceId, e);
+            }
+            iter.remove();
+            log.info("Removed MEP {} from Device {} because MD {} was deleted",
+                    mep.mepId(), mepDeviceId, mdId.mdName());
+        }
+
+        deviceIdsRemoved.forEach(deviceId -> {
+            try {
+                deviceService.getDevice(deviceId).as(CfmMepProgrammable.class)
+                        .deleteMdOnDevice(mdId, Optional.of(oldMd));
+            } catch (CfmConfigException e) {
+                log.warn("Could not delete MD {} from Device {}",
+                        mdId.mdName(), deviceId, e);
+            }
+        });
+    }
+
+    private class InternalStoreDelegate implements MepStoreDelegate {
+        @Override
+        public void notify(CfmMepEvent event) {
+            log.debug("New Mep event: {}", event);
+            eventDispatcher.post(event);
+        }
+    }
 }
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/DistributedMdStore.java b/incubator/net/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/DistributedMdStore.java
index 964a56a..82c550d 100644
--- a/incubator/net/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/DistributedMdStore.java
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/DistributedMdStore.java
@@ -19,6 +19,7 @@
 import org.apache.commons.lang3.tuple.ImmutablePair;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
@@ -42,6 +43,7 @@
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdMacUint;
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdNone;
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
 import org.onosproject.incubator.net.l2monitoring.cfm.service.MdEvent;
 import org.onosproject.incubator.net.l2monitoring.cfm.service.MdStore;
 import org.onosproject.incubator.net.l2monitoring.cfm.service.MdStoreDelegate;
@@ -58,6 +60,8 @@
 import java.util.Collection;
 import java.util.Map;
 import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * Maintenance Domain Store implementation backed by consistent map.
@@ -75,7 +79,7 @@
     private ConsistentMap<MdId, MaintenanceDomain> maintenanceDomainConsistentMap;
     private Map<MdId, MaintenanceDomain> maintenanceDomainMap;
 
-    private final InternalMdListener listener = new InternalMdListener();
+    private MapEventListener<MdId, MaintenanceDomain> mapListener = null;
 
     @Activate
     public void activate() {
@@ -112,8 +116,17 @@
                                             .cfm.Component.TagType.class)
                         .build("md")))
                 .build();
+        mapListener = new InternalMdListener();
+        maintenanceDomainConsistentMap.addListener(mapListener);
 
         maintenanceDomainMap = maintenanceDomainConsistentMap.asJavaMap();
+        log.info("MDStore started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        maintenanceDomainConsistentMap.removeListener(mapListener);
+        log.info("Stopped");
     }
 
     @Override
@@ -141,19 +154,65 @@
         @Override
         public void event(MapEvent<MdId, MaintenanceDomain> mapEvent) {
             final MdEvent.Type type;
+            MaIdShort maId = null;
             switch (mapEvent.type()) {
                 case INSERT:
                     type = MdEvent.Type.MD_ADDED;
                     break;
                 case UPDATE:
-                    type = MdEvent.Type.MD_UPDATED;
+                    // Examine the diff to see if it was a removal or addition of an MA caused it
+                    if (mapEvent.oldValue().value().maintenanceAssociationList().size() >
+                            mapEvent.newValue().value().maintenanceAssociationList().size()) {
+                        Set<MaIdShort> newMaIds = mapEvent.newValue().value().maintenanceAssociationList()
+                                .stream()
+                                .map(MaintenanceAssociation::maId)
+                                .collect(Collectors.toSet());
+                        Optional<MaintenanceAssociation> removedMa =
+                                mapEvent.oldValue().value().maintenanceAssociationList()
+                                        .stream()
+                                        .filter(maOld -> !newMaIds.contains(maOld.maId())).findFirst();
+                        if (removedMa.isPresent()) {
+                            maId = removedMa.get().maId();
+                        }
+                        type = MdEvent.Type.MA_REMOVED;
+                    } else if (mapEvent.oldValue().value().maintenanceAssociationList().size() <
+                        mapEvent.newValue().value().maintenanceAssociationList().size()) {
+                        Set<MaIdShort> oldMaIds = mapEvent.oldValue().value().maintenanceAssociationList()
+                                .stream()
+                                .map(MaintenanceAssociation::maId)
+                                .collect(Collectors.toSet());
+                        Optional<MaintenanceAssociation> addedMa =
+                                mapEvent.newValue().value().maintenanceAssociationList()
+                                        .stream()
+                                        .filter(maNew -> !oldMaIds.contains(maNew.maId())).findFirst();
+                        if (addedMa.isPresent()) {
+                            maId = addedMa.get().maId();
+                        }
+                        type = MdEvent.Type.MA_ADDED;
+                    } else {
+                        type = MdEvent.Type.MD_UPDATED;
+                    }
                     break;
                 case REMOVE:
                 default:
                     type = MdEvent.Type.MD_REMOVED;
                     break;
             }
-            notifyDelegate(new MdEvent(type, mapEvent.key()));
+            if (mapEvent.oldValue() != null && mapEvent.oldValue().value() != null) {
+                MaintenanceDomain oldMd = mapEvent.oldValue().value();
+                try {
+                    if (maId != null) {
+                        notifyDelegate(new MdEvent(type, mapEvent.key(), oldMd, maId));
+                    } else {
+                        notifyDelegate(new MdEvent(type, mapEvent.key(), oldMd));
+                    }
+                } catch (CfmConfigException e) {
+                    log.warn("Unable to copy MD {}", oldMd);
+                    notifyDelegate(new MdEvent(type, mapEvent.key()));
+                }
+            } else {
+                notifyDelegate(new MdEvent(type, mapEvent.key()));
+            }
         }
     }
 }
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/DistributedMepStore.java b/incubator/net/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/DistributedMepStore.java
new file mode 100644
index 0000000..c21166a
--- /dev/null
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/DistributedMepStore.java
@@ -0,0 +1,193 @@
+/*
+ * 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.incubator.net.l2monitoring.cfm.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.VlanId;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMep;
+import org.onosproject.incubator.net.l2monitoring.cfm.Mep;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaId2Octet;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdIccY1731;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdPrimaryVid;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdRfc2685VpnId;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdDomainName;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdMacUint;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdNone;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepKeyId;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepEvent;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.MepStore;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.MepStoreDelegate;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.NetworkResource;
+import org.onosproject.net.PortNumber;
+import org.onosproject.store.AbstractStore;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.MapEvent;
+import org.onosproject.store.service.MapEventListener;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.time.Duration;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * MEP Store implementation backed by consistent map.
+ */
+@Component(immediate = true)
+@Service
+public class DistributedMepStore extends AbstractStore<CfmMepEvent, MepStoreDelegate>
+    implements MepStore {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected StorageService storageService;
+
+    private ConsistentMap<MepKeyId, Mep> mepConsistentMap;
+    private Map<MepKeyId, Mep> mepMap;
+
+    private MapEventListener<MepKeyId, Mep> mapListener = null;
+
+    @Activate
+    public void activate() {
+        mepConsistentMap = storageService
+                .<MepKeyId, Mep>consistentMapBuilder()
+                .withName("onos-cfm-mep-map")
+                .withSerializer(Serializer.using(new KryoNamespace.Builder()
+                        .register(KryoNamespaces.API)
+                        .register(DefaultMep.class)
+                        .register(MepId.class)
+                        .register(MepKeyId.class)
+                        .register(NetworkResource.class)
+                        .register(DeviceId.class)
+                        .register(PortNumber.class)
+                        .register(Mep.MepDirection.class)
+                        .register(VlanId.class)
+                        .register(Mep.Priority.class)
+                        .register(Mep.FngAddress.class)
+                        .register(Mep.FngAddressType.class)
+                        .register(IpAddress.class)
+                        .register(Mep.LowestFaultDefect.class)
+                        .register(Duration.class)
+                        .register(MdIdCharStr.class)
+                        .register(MdIdDomainName.class)
+                        .register(MdIdMacUint.class)
+                        .register(MdIdNone.class)
+                        .register(MaIdCharStr.class)
+                        .register(MaIdShort.class)
+                        .register(MaId2Octet.class)
+                        .register(MaIdIccY1731.class)
+                        .register(MaIdPrimaryVid.class)
+                        .register(MaIdRfc2685VpnId.class)
+                        .build("mep")))
+                .build();
+        mapListener = new InternalMepListener();
+        mepConsistentMap.addListener(mapListener);
+
+        mepMap = mepConsistentMap.asJavaMap();
+        log.info("MepStore started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        mepConsistentMap.removeListener(mapListener);
+        log.info("MepStore stopped");
+    }
+
+    @Override
+    public Collection<Mep> getAllMeps() {
+        return mepMap.values();
+    }
+
+    @Override
+    public Collection<Mep> getMepsByMd(MdId mdName) {
+        return mepMap.values().stream()
+                .filter(mep -> mep.mdId().equals(mdName))
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public Collection<Mep> getMepsByMdMa(MdId mdName, MaIdShort maName) {
+        return mepMap.values().stream()
+                .filter(mep -> mep.mdId().equals(mdName) && mep.maId().equals(maName))
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public Collection<Mep> getMepsByDeviceId(DeviceId deviceId) {
+        return mepMap.values().stream()
+                .filter(mep -> mep.deviceId().equals(deviceId))
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public Optional<Mep> getMep(MepKeyId mepKeyId) {
+        return mepMap.values().stream()
+                .filter(mep -> mep.mdId().equals(mepKeyId.mdId()) &&
+                        mep.maId().equals(mepKeyId.maId()) &&
+                        mep.mepId().equals(mepKeyId.mepId()))
+                .findFirst();
+    }
+
+    @Override
+    public boolean deleteMep(MepKeyId mepKeyId) {
+        return mepMap.remove(mepKeyId) == null ? false : true;
+    }
+
+    @Override
+    public boolean createUpdateMep(MepKeyId mepKeyId, Mep mep) {
+        return mepMap.put(mepKeyId, mep) == null ? false : true;
+    }
+
+    private class InternalMepListener implements MapEventListener<MepKeyId, Mep> {
+
+        @Override
+        public void event(MapEvent<MepKeyId, Mep> mapEvent) {
+            final CfmMepEvent.Type type;
+
+            switch (mapEvent.type()) {
+                case INSERT:
+                    type = CfmMepEvent.Type.MEP_ADDED;
+                    break;
+                case UPDATE:
+                    type = CfmMepEvent.Type.MEP_UPDATED;
+                    break;
+                default:
+                case REMOVE:
+                    type = CfmMepEvent.Type.MEP_REMOVED;
+            }
+            notifyDelegate(new CfmMepEvent(type, mapEvent.key()));
+        }
+    }
+}
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/resource/label/impl/LabelResourceManager.java b/incubator/net/src/main/java/org/onosproject/incubator/net/resource/label/impl/LabelResourceManager.java
index 259dce0..56c308e 100644
--- a/incubator/net/src/main/java/org/onosproject/incubator/net/resource/label/impl/LabelResourceManager.java
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/resource/label/impl/LabelResourceManager.java
@@ -130,13 +130,11 @@
     public Collection<LabelResource> applyFromDevicePool(DeviceId deviceId,
                                                          long applyNum) {
         checkNotNull(deviceId, "deviceId is not null");
-        checkNotNull(applyNum, "applyNum is not null");
         return store.applyFromDevicePool(deviceId, applyNum);
     }
 
     @Override
     public Collection<LabelResource> applyFromGlobalPool(long applyNum) {
-        checkNotNull(applyNum, "applyNum is not null");
         return store.applyFromGlobalPool(applyNum);
     }
 
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkFlowObjectiveManager.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkFlowObjectiveManager.java
index 1816bb4..2a5bc26 100644
--- a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkFlowObjectiveManager.java
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkFlowObjectiveManager.java
@@ -317,7 +317,7 @@
         public ObjectiveInstaller(DeviceId deviceId, Objective objective, int attemps) {
             this.deviceId = checkNotNull(deviceId);
             this.objective = checkNotNull(objective);
-            this.numAttempts = checkNotNull(attemps);
+            this.numAttempts = attemps;
         }
 
         @Override
diff --git a/incubator/net/src/test/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/CfmMdManagerTest.java b/incubator/net/src/test/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/CfmMdManagerTest.java
index a0de513..fa79546 100644
--- a/incubator/net/src/test/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/CfmMdManagerTest.java
+++ b/incubator/net/src/test/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/CfmMdManagerTest.java
@@ -36,10 +36,13 @@
 import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceAssociation;
 import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceDomain;
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdCharStr;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId;
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr;
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
 import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
 import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMdService;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepService;
 import org.onosproject.mastership.MastershipServiceAdapter;
 import org.onosproject.net.DeviceId;
 import org.onosproject.store.service.TestStorageService;
@@ -50,6 +53,7 @@
 import java.util.concurrent.atomic.AtomicLong;
 
 import static junit.framework.TestCase.assertFalse;
+import static org.easymock.EasyMock.createMock;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -61,6 +65,11 @@
 public class CfmMdManagerTest {
     private static final NodeId NID_LOCAL = new NodeId("local");
     private static final IpAddress LOCALHOST = IpAddress.valueOf("127.0.0.1");
+    private static final MaIdShort MA_ID_1_1 = MaIdCharStr.asMaId("test-ma-1-1");
+    private static final MaIdShort MA_ID_1_2 = MaIdCharStr.asMaId("test-ma-1-2");
+    private static final MdId MD_ID_1 = MdIdCharStr.asMdId("test-md-1");
+
+    private final CfmMepService mepService = createMock(CfmMepService.class);
 
     private DistributedMdStore mdStore;
     private CfmMdService service;
@@ -71,7 +80,7 @@
         mdStore = new DistributedMdStore();
 
         MaintenanceAssociation maTest11 = DefaultMaintenanceAssociation
-                .builder(MaIdCharStr.asMaId("test-ma-1-1"), 9)
+                .builder(MA_ID_1_1, MD_ID_1.getNameLength())
                 .ccmInterval(MaintenanceAssociation.CcmInterval.INTERVAL_10MIN)
                 .maNumericId((short) 1)
                 .addToRemoteMepIdList(MepId.valueOf((short) 101))
@@ -83,7 +92,7 @@
                 .build();
 
         MaintenanceAssociation maTest12 = DefaultMaintenanceAssociation
-                .builder(MaIdCharStr.asMaId("test-ma-1-2"), 9)
+                .builder(MA_ID_1_2, MD_ID_1.getNameLength())
                 .ccmInterval(MaintenanceAssociation.CcmInterval.INTERVAL_10MIN)
                 .maNumericId((short) 2)
                 .addToRemoteMepIdList(MepId.valueOf((short) 201))
@@ -95,7 +104,7 @@
                 .build();
 
         MaintenanceDomain mdTest1 = DefaultMaintenanceDomain
-                .builder(MdIdCharStr.asMdId("test-md-1"))
+                .builder(MD_ID_1)
                 .mdLevel(MaintenanceDomain.MdLevel.LEVEL1)
                 .mdNumericId((short) 1)
                 .addToMaList(maTest11)
@@ -105,6 +114,7 @@
         TestUtils.setField(mdStore, "storageService", new TestStorageService());
         TestUtils.setField(mdStore, "clusterService", new CfmMdManagerTest.TestClusterService());
         TestUtils.setField(mdStore, "mastershipService", new CfmMdManagerTest.TestMastershipService());
+
         mdStore.activate();
         mdStore.createUpdateMaintenanceDomain(mdTest1);
 
@@ -113,6 +123,7 @@
         service = manager;
         TestUtils.setField(manager, "storageService", new TestStorageService());
         TestUtils.setField(manager, "coreService", new TestCoreService());
+        TestUtils.setField(manager, "mepService", mepService);
         injectEventDispatcher(manager, new TestEventDispatcher());
 
         manager.appId = new CfmMdManagerTest.TestApplicationId(0, "CfmMdManagerTest");
@@ -339,7 +350,6 @@
                 MdIdCharStr.asMdId("test-md-1")).size());
     }
 
-
     public class TestApplicationId extends DefaultApplicationId {
         public TestApplicationId(int id, String name) {
             super(id, name);
diff --git a/incubator/net/src/test/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/CfmMepManagerTest.java b/incubator/net/src/test/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/CfmMepManagerTest.java
index a825858..5941f6f 100644
--- a/incubator/net/src/test/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/CfmMepManagerTest.java
+++ b/incubator/net/src/test/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/CfmMepManagerTest.java
@@ -37,10 +37,12 @@
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId;
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr;
 import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId;
+import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepKeyId;
 import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException;
 import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMdService;
 import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepProgrammable;
 import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepService;
+import org.onosproject.incubator.net.l2monitoring.cfm.service.MepStore;
 import org.onosproject.incubator.net.l2monitoring.soam.SoamDmProgrammable;
 import org.onosproject.incubator.net.l2monitoring.soam.impl.TestSoamDmProgrammable;
 import org.onosproject.net.AbstractProjectableModel;
@@ -57,21 +59,30 @@
 import org.onosproject.net.driver.Driver;
 import org.onosproject.net.driver.DriverService;
 import org.onosproject.net.provider.ProviderId;
+import org.onosproject.store.service.AsyncDocumentTree;
+import org.onosproject.store.service.DocumentTreeBuilder;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.TestAsyncDocumentTree;
+import org.onosproject.store.service.TestStorageService;
+import org.onosproject.store.service.TestTopic;
+import org.onosproject.store.service.Topic;
 
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.concurrent.atomic.AtomicLong;
 
-import static junit.framework.TestCase.fail;
 import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.replay;
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
 import static org.onosproject.net.NetTestTools.injectEventDispatcher;
 
 /**
@@ -91,18 +102,32 @@
 
     private CfmMepService mepService;
     private CfmMepManager mepManager;
+    private MepStore mepStore;
+    private StorageService storageService;
 
     protected static final MdId MDNAME1 = MdIdCharStr.asMdId("md-1");
+    protected static final MdId MDNAME2 = MdIdCharStr.asMdId("md-2");
     protected static final MaIdShort MANAME1 = MaIdCharStr.asMaId("ma-1-1");
+    protected static final MaIdShort MANAME2 = MaIdCharStr.asMaId("ma-2-2");
 
     private MaintenanceAssociation ma1;
+    private MaintenanceAssociation ma2;
     protected static final MepId MEPID1 = MepId.valueOf((short) 10);
+    protected static final MepId MEPID11 = MepId.valueOf((short) 11);
+    protected static final MepId MEPID12 = MepId.valueOf((short) 12);
     protected static final MepId MEPID2 = MepId.valueOf((short) 20);
+    protected static final MepId MEPID21 = MepId.valueOf((short) 21);
+    protected static final MepId MEPID22 = MepId.valueOf((short) 22);
+
     protected static final DeviceId DEVICE_ID1 = DeviceId.deviceId("netconf:1.2.3.4:830");
     protected static final DeviceId DEVICE_ID2 = DeviceId.deviceId("netconf:2.2.3.4:830");
 
     private Mep mep1;
+    private Mep mep11;
+    private Mep mep12;
     private Mep mep2;
+    private Mep mep21;
+    private Mep mep22;
 
     private Device device1;
     private Device device2;
@@ -112,12 +137,19 @@
     @Before
     public void setup() throws CfmConfigException {
         mepManager = new CfmMepManager();
+        mepStore = new DistributedMepStore();
+        storageService = new MockStorageService();
 
         ma1 = DefaultMaintenanceAssociation.builder(MANAME1, MDNAME1.getNameLength()).build();
+        ma2 = DefaultMaintenanceAssociation.builder(MANAME2, MDNAME2.getNameLength()).build();
+
+        TestUtils.setField(mepStore, "storageService", storageService);
+        ((DistributedMepStore) mepStore).activate();
 
         TestUtils.setField(mepManager, "coreService", new TestCoreService());
         TestUtils.setField(mepManager, "deviceService", deviceService);
         TestUtils.setField(mepManager, "cfmMdService", mdService);
+        TestUtils.setField(mepManager, "mepStore", mepStore);
         injectEventDispatcher(mepManager, new TestEventDispatcher());
 
         mepService = mepManager;
@@ -125,12 +157,27 @@
 
         mep1 = DefaultMep.builder(MEPID1, DEVICE_ID1, PortNumber.P0,
                 Mep.MepDirection.UP_MEP, MDNAME1, MANAME1).build();
+        mepStore.createUpdateMep(new MepKeyId(MDNAME1, MANAME1, MEPID1), mep1);
+
+        mep11 = DefaultMep.builder(MEPID11, DEVICE_ID1, PortNumber.P0,
+                Mep.MepDirection.UP_MEP, MDNAME1, MANAME1).build();
+        mepStore.createUpdateMep(new MepKeyId(MDNAME1, MANAME1, MEPID11), mep11);
+
+        mep12 = DefaultMep.builder(MEPID12, DEVICE_ID1, PortNumber.P0,
+                Mep.MepDirection.UP_MEP, MDNAME2, MANAME2).build();
+        mepStore.createUpdateMep(new MepKeyId(MDNAME2, MANAME2, MEPID12), mep12);
+
         mep2 = DefaultMep.builder(MEPID2, DEVICE_ID2, PortNumber.portNumber(2),
                 Mep.MepDirection.UP_MEP, MDNAME1, MANAME1).build();
-        List<Mep> mepList = new ArrayList<>();
-        mepList.add(mep1);
-        mepList.add(mep2);
-        TestUtils.setField(mepManager, "mepCollection", mepList);
+        mepStore.createUpdateMep(new MepKeyId(MDNAME1, MANAME1, MEPID2), mep2);
+
+        mep21 = DefaultMep.builder(MEPID21, DEVICE_ID2, PortNumber.portNumber(2),
+                Mep.MepDirection.UP_MEP, MDNAME1, MANAME1).build();
+        mepStore.createUpdateMep(new MepKeyId(MDNAME1, MANAME1, MEPID21), mep21);
+
+        mep22 = DefaultMep.builder(MEPID22, DEVICE_ID2, PortNumber.portNumber(2),
+                Mep.MepDirection.UP_MEP, MDNAME2, MANAME2).build();
+        mepStore.createUpdateMep(new MepKeyId(MDNAME2, MANAME2, MEPID22), mep22);
 
         device1 = new DefaultDevice(
                 ProviderId.NONE, DEVICE_ID1, Device.Type.SWITCH,
@@ -180,7 +227,7 @@
 
         Collection<MepEntry> mepEntries = mepManager.getAllMeps(MDNAME1, MANAME1);
 
-        assertEquals(2, mepEntries.size());
+        assertEquals(4, mepEntries.size());
     }
 
     @Test
@@ -232,12 +279,13 @@
         replay(mdService);
 
         expect(deviceService.getDevice(DEVICE_ID1)).andReturn(device1).anyTimes();
+        expect(deviceService.getDevice(DEVICE_ID2)).andReturn(device2).anyTimes();
         replay(deviceService);
 
         expect(driverService.getDriver(TEST_DRIVER)).andReturn(testDriver).anyTimes();
         replay(driverService);
 
-        assertTrue(mepManager.deleteMep(MDNAME1, MANAME1, MEPID1));
+        assertTrue(mepManager.deleteMep(MDNAME1, MANAME1, MEPID1, Optional.empty()));
     }
 
     @Test
@@ -248,6 +296,7 @@
         replay(mdService);
 
         expect(deviceService.getDevice(DEVICE_ID1)).andReturn(device1).anyTimes();
+        expect(deviceService.getDevice(DEVICE_ID2)).andReturn(device2).anyTimes();
         replay(deviceService);
 
         expect(driverService.getDriver(TEST_DRIVER)).andReturn(testDriver).anyTimes();
@@ -257,6 +306,7 @@
         Mep mep3 = DefaultMep.builder(mepId3, DEVICE_ID1, PortNumber.portNumber(1),
                 Mep.MepDirection.UP_MEP, MDNAME1, MANAME1).build();
 
+        //Expecting false - since it was not found
         assertTrue(mepManager.createMep(MDNAME1, MANAME1, mep3));
     }
 
@@ -364,6 +414,56 @@
         }
     }
 
+    @Test
+    public void testDeviceRemoved() throws CfmConfigException {
+        expect(mdService.getMaintenanceAssociation(MDNAME1, MANAME1))
+                .andReturn(Optional.ofNullable(ma1))
+                .anyTimes();
+        expect(mdService.getMaintenanceAssociation(MDNAME2, MANAME2))
+                .andReturn(Optional.ofNullable(ma2))
+                .anyTimes();
+        replay(mdService);
+
+        expect(deviceService.getDevice(DEVICE_ID1)).andReturn(device1).anyTimes();
+        expect(deviceService.getDevice(DEVICE_ID2)).andReturn(device2).anyTimes();
+        replay(deviceService);
+
+        expect(driverService.getDriver(TEST_DRIVER)).andReturn(testDriver).anyTimes();
+        replay(driverService);
+
+//        This is arranged like
+//        device1                             device2
+//        /       \                           /       \
+//    md-1        md-2                     md-1      md-2
+//      |           |                         |        |
+//    ma-1-1      ma-2-2                   ma-1-1    ma-2-2
+//    /    \         |                      /   \        \
+//  mep1  mep11   mep12                  mep2 mep21     mep22
+        assertNotNull(mepService.getMep(MDNAME1, MANAME1, MEPID1));
+        assertNotNull(mepService.getMep(MDNAME1, MANAME1, MEPID11));
+        assertNotNull(mepService.getMep(MDNAME2, MANAME2, MEPID12));
+        assertNotNull(mepService.getMep(MDNAME1, MANAME1, MEPID2));
+        assertNotNull(mepService.getMep(MDNAME1, MANAME1, MEPID21));
+        assertNotNull(mepService.getMep(MDNAME2, MANAME2, MEPID22));
+
+        //By deleting Device2 we expect Mep2,21,22 to have been deleted but Mep1,11,12 to remain
+        ((CfmMepManager) mepService).processDeviceRemoved(device2);
+
+        assertNotNull(mepService.getMep(MDNAME1, MANAME1, MEPID1));
+        assertNotNull(mepService.getMep(MDNAME1, MANAME1, MEPID11));
+        assertNotNull(mepService.getMep(MDNAME2, MANAME2, MEPID12));
+        //The device 2 related ones are gone
+        assertNull(mepService.getMep(MDNAME1, MANAME1, MEPID2));
+        assertNull(mepService.getMep(MDNAME1, MANAME1, MEPID21));
+        assertNull(mepService.getMep(MDNAME2, MANAME2, MEPID22));
+
+        //Now delete device1
+        ((CfmMepManager) mepService).processDeviceRemoved(device1);
+        assertNull(mepService.getMep(MDNAME1, MANAME1, MEPID1));
+        assertNull(mepService.getMep(MDNAME1, MANAME1, MEPID11));
+        assertNull(mepService.getMep(MDNAME2, MANAME2, MEPID12));
+    }
+
     private class TestCoreService extends CoreServiceAdapter {
 
         @Override
@@ -378,4 +478,27 @@
             };
         }
     }
+
+    private static class MockStorageService extends TestStorageService {
+        @Override
+        public <V> DocumentTreeBuilder<V> documentTreeBuilder() {
+            return new DocumentTreeBuilder<V>() {
+                @Override
+                public AsyncDocumentTree<V> buildDocumentTree() {
+                    return build();
+                }
+
+                @Override
+                @SuppressWarnings("unchecked")
+                public AsyncDocumentTree<V> build() {
+                    return new TestAsyncDocumentTree<>(name());
+                }
+            };
+        }
+
+        @Override
+        public <T> Topic<T> getTopic(String name, Serializer serializer) {
+            return new TestTopic<>(name);
+        }
+    }
 }
diff --git a/incubator/net/src/test/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/TestCfmMepProgrammable.java b/incubator/net/src/test/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/TestCfmMepProgrammable.java
index 8d0f765..3f4be0e 100644
--- a/incubator/net/src/test/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/TestCfmMepProgrammable.java
+++ b/incubator/net/src/test/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/TestCfmMepProgrammable.java
@@ -17,6 +17,7 @@
 
 import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMep;
 import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMepEntry;
+import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceDomain;
 import org.onosproject.incubator.net.l2monitoring.cfm.Mep;
 import org.onosproject.incubator.net.l2monitoring.cfm.MepEntry;
 import org.onosproject.incubator.net.l2monitoring.cfm.MepLbCreate;
@@ -30,8 +31,8 @@
 import org.onosproject.net.driver.AbstractHandlerBehaviour;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.List;
+import java.util.Optional;
 
 import static org.onosproject.incubator.net.l2monitoring.cfm.impl.CfmMepManagerTest.*;
 
@@ -47,13 +48,16 @@
 
         deviceMepList.add(DefaultMep.builder(MEPID1, DEVICE_ID1, PortNumber.P0,
                 Mep.MepDirection.UP_MEP, MDNAME1, MANAME1).build());
+        deviceMepList.add(DefaultMep.builder(MEPID11, DEVICE_ID1, PortNumber.P0,
+                Mep.MepDirection.UP_MEP, MDNAME1, MANAME1).build());
+        deviceMepList.add(DefaultMep.builder(MEPID12, DEVICE_ID1, PortNumber.P0,
+                Mep.MepDirection.UP_MEP, MDNAME2, MANAME2).build());
         deviceMepList.add(DefaultMep.builder(MEPID2, DEVICE_ID2, PortNumber.portNumber(2),
                 Mep.MepDirection.UP_MEP, MDNAME1, MANAME1).build());
-    }
-
-    @Override
-    public Collection<MepEntry> getAllMeps(MdId mdName, MaIdShort maName) throws CfmConfigException {
-        return null;
+        deviceMepList.add(DefaultMep.builder(MEPID21, DEVICE_ID2, PortNumber.portNumber(2),
+                Mep.MepDirection.UP_MEP, MDNAME1, MANAME1).build());
+        deviceMepList.add(DefaultMep.builder(MEPID22, DEVICE_ID2, PortNumber.portNumber(2),
+                Mep.MepDirection.UP_MEP, MDNAME2, MANAME2).build());
     }
 
     @Override
@@ -67,7 +71,8 @@
     }
 
     @Override
-    public boolean deleteMep(MdId mdName, MaIdShort maName, MepId mepId) throws CfmConfigException {
+    public boolean deleteMep(MdId mdName, MaIdShort maName, MepId mepId,
+                             Optional<MaintenanceDomain> oldMd) throws CfmConfigException {
         return true;
     }
 
@@ -77,6 +82,37 @@
     }
 
     @Override
+    public boolean deleteMdOnDevice(MdId mdId, Optional<MaintenanceDomain> oldMd) throws CfmConfigException {
+        return false;
+    }
+
+    @Override
+    public boolean deleteMaOnDevice(MdId mdId, MaIdShort maId,
+                                    Optional<MaintenanceDomain> oldMd) throws CfmConfigException {
+        return false;
+    }
+
+    @Override
+    public boolean createMdOnDevice(MdId mdId) throws CfmConfigException {
+        return false;
+    }
+
+    @Override
+    public boolean createMaOnDevice(MdId mdId, MaIdShort maId) throws CfmConfigException {
+        return false;
+    }
+
+    @Override
+    public boolean createMaRemoteMepOnDevice(MdId mdId, MaIdShort maId, MepId remoteMep) throws CfmConfigException {
+        return false;
+    }
+
+    @Override
+    public boolean deleteMaRemoteMepOnDevice(MdId mdId, MaIdShort maId, MepId remoteMep) throws CfmConfigException {
+        return false;
+    }
+
+    @Override
     public void transmitLoopback(MdId mdName, MaIdShort maName, MepId mepId, MepLbCreate lbCreate)
             throws CfmConfigException {
 
diff --git a/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkIntentManagerTest.java b/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkIntentManagerTest.java
index 9d6e547..c7d21cc 100644
--- a/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkIntentManagerTest.java
+++ b/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkIntentManagerTest.java
@@ -25,7 +25,6 @@
 import org.onlab.junit.TestUtils;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
-import org.onlab.rest.BaseResource;
 import org.onosproject.TestApplicationId;
 import org.onosproject.common.event.impl.TestEventDispatcher;
 import org.onosproject.core.ApplicationId;
@@ -76,7 +75,10 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicLong;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 /**
  * Junit tests for VirtualNetworkIntentService.
@@ -149,7 +151,6 @@
                 .add(VirtualNetworkStore.class, virtualNetworkManagerStore)
                 .add(IntentService.class, intentService)
                 .add(WorkPartitionService.class, workPartitionService);
-        BaseResource.setServiceDirectory(testDirectory);
         TestUtils.setField(manager, "serviceDirectory", testDirectory);
 
         manager.activate();
diff --git a/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkTopologyManagerTest.java b/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkTopologyManagerTest.java
index 38d2e82..6218a15 100644
--- a/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkTopologyManagerTest.java
+++ b/incubator/net/src/test/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkTopologyManagerTest.java
@@ -21,7 +21,6 @@
 import org.junit.Test;
 import org.onlab.junit.TestUtils;
 import org.onlab.osgi.TestServiceDirectory;
-import org.onlab.rest.BaseResource;
 import org.onosproject.common.event.impl.TestEventDispatcher;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
@@ -55,7 +54,9 @@
 import java.util.concurrent.atomic.AtomicLong;
 
 import static junit.framework.TestCase.assertTrue;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 
 /**
  * Junit tests for VirtualNetworkTopologyService.
@@ -77,7 +78,6 @@
         TestUtils.setField(virtualNetworkManagerStore, "storageService", new TestStorageService());
         virtualNetworkManagerStore.activate();
 
-        BaseResource.setServiceDirectory(testDirectory);
         manager = new VirtualNetworkManager();
         manager.store = virtualNetworkManagerStore;
         manager.coreService = coreService;
diff --git a/incubator/protobuf/services/nb/src/main/java/org/onosproject/incubator/protobuf/services/nb/GrpcNbApplicationService.java b/incubator/protobuf/services/nb/src/main/java/org/onosproject/incubator/protobuf/services/nb/GrpcNbApplicationService.java
index bce4406..7ec8ed1 100644
--- a/incubator/protobuf/services/nb/src/main/java/org/onosproject/incubator/protobuf/services/nb/GrpcNbApplicationService.java
+++ b/incubator/protobuf/services/nb/src/main/java/org/onosproject/incubator/protobuf/services/nb/GrpcNbApplicationService.java
@@ -59,7 +59,7 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected ApplicationService applicationService;
 
-    private static ApplicationServiceNbServerInternal instance = null;
+    private ApplicationServiceNbServerInternal instance = null;
 
 
     @Activate
diff --git a/incubator/protobuf/services/nb/src/main/java/org/onosproject/incubator/protobuf/services/nb/GrpcNbComponentConfigService.java b/incubator/protobuf/services/nb/src/main/java/org/onosproject/incubator/protobuf/services/nb/GrpcNbComponentConfigService.java
index 8e0c2dd..673d126 100644
--- a/incubator/protobuf/services/nb/src/main/java/org/onosproject/incubator/protobuf/services/nb/GrpcNbComponentConfigService.java
+++ b/incubator/protobuf/services/nb/src/main/java/org/onosproject/incubator/protobuf/services/nb/GrpcNbComponentConfigService.java
@@ -62,7 +62,7 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected ComponentConfigService componentConfigService;
 
-    private static ComponentConfigServiceNbServerInternal instance = null;
+    private ComponentConfigServiceNbServerInternal instance = null;
 
     @Activate
     public void activate() {
diff --git a/incubator/protobuf/services/nb/src/main/java/org/onosproject/incubator/protobuf/services/nb/GrpcNbDeviceService.java b/incubator/protobuf/services/nb/src/main/java/org/onosproject/incubator/protobuf/services/nb/GrpcNbDeviceService.java
index b583800..a06acf6 100644
--- a/incubator/protobuf/services/nb/src/main/java/org/onosproject/incubator/protobuf/services/nb/GrpcNbDeviceService.java
+++ b/incubator/protobuf/services/nb/src/main/java/org/onosproject/incubator/protobuf/services/nb/GrpcNbDeviceService.java
@@ -52,7 +52,7 @@
 
     private final Logger log = getLogger(getClass());
 
-    private static DeviceServiceNbServerInternal instance = null;
+    private DeviceServiceNbServerInternal instance = null;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected GrpcServiceRegistry registry;
diff --git a/incubator/protobuf/services/nb/src/main/java/org/onosproject/incubator/protobuf/services/nb/GrpcNbHostService.java b/incubator/protobuf/services/nb/src/main/java/org/onosproject/incubator/protobuf/services/nb/GrpcNbHostService.java
index d5e3660..2ad2bf6 100644
--- a/incubator/protobuf/services/nb/src/main/java/org/onosproject/incubator/protobuf/services/nb/GrpcNbHostService.java
+++ b/incubator/protobuf/services/nb/src/main/java/org/onosproject/incubator/protobuf/services/nb/GrpcNbHostService.java
@@ -74,7 +74,7 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected HostService hostService;
 
-    private static HostServiceNBServerInternal instance = null;
+    private HostServiceNBServerInternal instance = null;
 
     @Activate
     public void activate() {
diff --git a/incubator/protobuf/services/nb/src/main/java/org/onosproject/incubator/protobuf/services/nb/GrpcNbMastershipService.java b/incubator/protobuf/services/nb/src/main/java/org/onosproject/incubator/protobuf/services/nb/GrpcNbMastershipService.java
index 1495467..34b8cd9 100644
--- a/incubator/protobuf/services/nb/src/main/java/org/onosproject/incubator/protobuf/services/nb/GrpcNbMastershipService.java
+++ b/incubator/protobuf/services/nb/src/main/java/org/onosproject/incubator/protobuf/services/nb/GrpcNbMastershipService.java
@@ -63,7 +63,7 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected MastershipService mastershipService;
 
-    private static MastershipServiceNbServerInternal instance = null;
+    private MastershipServiceNbServerInternal instance = null;
 
     @Activate
     public void activate() {
diff --git a/incubator/store/src/main/java/org/onosproject/incubator/store/tunnel/impl/DistributedTunnelStore.java b/incubator/store/src/main/java/org/onosproject/incubator/store/tunnel/impl/DistributedTunnelStore.java
index fd0e95d..82f0140 100644
--- a/incubator/store/src/main/java/org/onosproject/incubator/store/tunnel/impl/DistributedTunnelStore.java
+++ b/incubator/store/src/main/java/org/onosproject/incubator/store/tunnel/impl/DistributedTunnelStore.java
@@ -227,8 +227,7 @@
         for (TunnelId id : idSet) {
             deletedTunnel = tunnelIdAsKeyStore.get(id);
 
-            if (producerName == null || (producerName != null
-                    && producerName.equals(deletedTunnel.providerId()))) {
+            if (producerName == null || producerName.equals(deletedTunnel.providerId())) {
                 tunnelIdAsKeyStore.remove(deletedTunnel.tunnelId());
 
                 event = new TunnelEvent(TunnelEvent.Type.TUNNEL_REMOVED,
@@ -256,8 +255,8 @@
         for (TunnelId id : idSet) {
             deletedTunnel = tunnelIdAsKeyStore.get(id);
 
-            if (type.equals(deletedTunnel.type()) && (producerName == null || (producerName != null
-                    && producerName.equals(deletedTunnel.providerId())))) {
+            if (type.equals(deletedTunnel.type()) && (producerName == null ||
+                    producerName.equals(deletedTunnel.providerId()))) {
                 tunnelIdAsKeyStore.remove(deletedTunnel.tunnelId());
 
                 event = new TunnelEvent(TunnelEvent.Type.TUNNEL_REMOVED,
diff --git a/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/DistributedVirtualNetworkStore.java b/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/DistributedVirtualNetworkStore.java
index 1eaf196..5209543 100644
--- a/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/DistributedVirtualNetworkStore.java
+++ b/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/DistributedVirtualNetworkStore.java
@@ -462,17 +462,15 @@
             }
         });
 
-        if (hostIdSet != null) {
-            networkIdHostIdSetMap.compute(networkId, (id, existingHostIds) -> {
-                if (existingHostIds == null || existingHostIds.isEmpty()) {
-                    return new HashSet<>();
-                } else {
-                    return new HashSet<>(Sets.difference(existingHostIds, hostIdSet));
-                }
-            });
+        networkIdHostIdSetMap.compute(networkId, (id, existingHostIds) -> {
+            if (existingHostIds == null || existingHostIds.isEmpty()) {
+                return new HashSet<>();
+            } else {
+                return new HashSet<>(Sets.difference(existingHostIds, hostIdSet));
+            }
+        });
 
-            hostIdVirtualHostMap.remove(hostId);
-        }
+        hostIdVirtualHostMap.remove(hostId);
     }
 
     /**
@@ -567,15 +565,13 @@
         Set<VirtualLink> virtualLinkSet = new HashSet<>();
         virtualLinkSet.add(virtualLink);
 
-        if (virtualLinkSet != null) {
-            networkIdVirtualLinkSetMap.compute(networkId, (id, existingVirtualLinks) -> {
-                if (existingVirtualLinks == null || existingVirtualLinks.isEmpty()) {
-                    return new HashSet<>();
-                } else {
-                    return new HashSet<>(Sets.difference(existingVirtualLinks, virtualLinkSet));
-                }
-            });
-        }
+        networkIdVirtualLinkSetMap.compute(networkId, (id, existingVirtualLinks) -> {
+            if (existingVirtualLinks == null || existingVirtualLinks.isEmpty()) {
+                return new HashSet<>();
+            } else {
+                return new HashSet<>(Sets.difference(existingVirtualLinks, virtualLinkSet));
+            }
+        });
         return virtualLink;
     }
 
diff --git a/lib/BUCK b/lib/BUCK
index 2c60230..860b5de 100644
--- a/lib/BUCK
+++ b/lib/BUCK
@@ -1,4 +1,4 @@
-# ***** This file was auto-generated at Fri, 5 Jan 2018 21:10:43 GMT. Do not edit this file manually. *****
+# ***** This file was auto-generated at Tue, 23 Jan 2018 20:38:59 GMT. Do not edit this file manually. *****
 # ***** Use onos-lib-gen *****
 
 pass_thru_pom(
@@ -207,10 +207,10 @@
 
 remote_jar (
   name = 'atomix',
-  out = 'atomix-2.0.12.jar',
-  url = 'mvn:io.atomix:atomix:jar:2.0.12',
-  sha1 = 'f2e814ea90812b843a1ad51b2003dda26267be17',
-  maven_coords = 'io.atomix:atomix:2.0.12',
+  out = 'atomix-2.0.14.jar',
+  url = 'mvn:io.atomix:atomix:jar:2.0.14',
+  sha1 = 'b2deb5601c8385c0eaabc952a19241c772f0984c',
+  maven_coords = 'io.atomix:atomix:2.0.14',
   visibility = [ 'PUBLIC' ],
 )
 
diff --git a/lib/deps.json b/lib/deps.json
index 544bf8e..0d98ca2 100644
--- a/lib/deps.json
+++ b/lib/deps.json
@@ -116,7 +116,7 @@
     "aopalliance-repackaged": "mvn:org.glassfish.hk2.external:aopalliance-repackaged:2.5.0-b32",
     "amqp-client": "mvn:com.rabbitmq:amqp-client:jar:3.6.1",
     "asm": "mvn:org.ow2.asm:asm:5.0.4",
-    "atomix": "mvn:io.atomix:atomix:2.0.12",
+    "atomix": "mvn:io.atomix:atomix:2.0.14",
     "commons-codec": "mvn:commons-codec:commons-codec:1.10",
     "commons-collections": "mvn:commons-collections:commons-collections:3.2.2",
     "commons-configuration": "mvn:commons-configuration:commons-configuration:1.10",
diff --git a/lib/pom.xml b/lib/pom.xml
index 7b8f528..8298544 100644
--- a/lib/pom.xml
+++ b/lib/pom.xml
@@ -704,19 +704,20 @@
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-compiler-plugin</artifactId>
-                    <!-- TODO: update once following issue is fixed. -->
-                    <!-- https://jira.codehaus.org/browse/MCOMPILER-205 -->
-                    <version>2.5.1</version>
+                    <version>3.7.0</version>
                     <configuration>
                         <source>1.8</source>
                         <target>1.8</target>
+                        <compilerArgs>
+                            <compilerArg>-Xpkginfo:always</compilerArg>
+                        </compilerArgs>
                     </configuration>
                 </plugin>
 
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-surefire-plugin</artifactId>
-                    <version>2.20</version>
+                    <version>2.20.1</version>
                     <configuration>
                         <redirectTestOutputToFile>true</redirectTestOutputToFile>
                         <printSummary>true</printSummary>
@@ -728,7 +729,7 @@
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-javadoc-plugin</artifactId>
-                    <version>2.10.4</version>
+                    <version>3.0.0</version>
                     <configuration>
                         <tags>
                             <tag>
@@ -775,7 +776,7 @@
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-shade-plugin</artifactId>
-                    <version>3.0.0</version>
+                    <version>3.1.0</version>
                 </plugin>
 
                 <plugin>
@@ -857,6 +858,14 @@
                         </execution>
                     </executions>
                 </plugin>
+
+                <plugin>
+                    <groupId>org.apache.karaf.tooling</groupId>
+                    <artifactId>karaf-maven-plugin</artifactId>
+                    <version>${karaf.version}</version>
+                    <extensions>true</extensions>
+                </plugin>
+
             </plugins>
         </pluginManagement>
 
@@ -869,7 +878,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-checkstyle-plugin</artifactId>
-                <version>2.17</version>
+                <version>3.0.0</version>
                 <dependencies>
                     <dependency>
                         <groupId>org.onosproject</groupId>
@@ -883,12 +892,6 @@
                     </dependency>
                 </dependencies>
                 <configuration>
-                    <!-- begin: workaround for unexpected NullPointerException on Eclipse -->
-                    <sourceDirectory>${project.build.sourceDirectory}
-                    </sourceDirectory>
-                    <testSourceDirectory>${project.build.testSourceDirectory}
-                    </testSourceDirectory>
-                    <!-- end: workaround for unexpected NullPointerException on Eclipse -->
                     <configLocation>onos/checkstyle-mvn.xml</configLocation>
                     <suppressionsLocation>onos/suppressions.xml
                     </suppressionsLocation>
@@ -935,7 +938,7 @@
             <plugin>
                 <groupId>org.jacoco</groupId>
                 <artifactId>jacoco-maven-plugin</artifactId>
-                <version>0.7.9</version>
+                <version>0.8.0</version>
                 <executions>
                     <execution>
                         <id>default-prepare-agent</id>
diff --git a/models/common/pom.xml b/models/common/pom.xml
index 18e4a22..030faf0 100644
--- a/models/common/pom.xml
+++ b/models/common/pom.xml
@@ -116,6 +116,17 @@
                 </executions>
             </plugin>
 
+            <!-- FIXME: YANG tool generates problematic code -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <compilerArgs>
+                      <arg>-XepDisableAllChecks</arg>
+                      <arg>-Xep:BetaApi:OFF</arg>
+                    </compilerArgs>
+                </configuration>
+            </plugin>
 
         </plugins>
 
diff --git a/models/microsemi/pom.xml b/models/microsemi/pom.xml
index 858828d..7712145 100644
--- a/models/microsemi/pom.xml
+++ b/models/microsemi/pom.xml
@@ -123,6 +123,17 @@
                 </executions>
             </plugin>
 
+            <!-- FIXME: YANG tool generates problematic code -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <compilerArgs>
+                      <arg>-XepDisableAllChecks</arg>
+                      <arg>-Xep:BetaApi:OFF</arg>
+                    </compilerArgs>
+                </configuration>
+            </plugin>
 
         </plugins>
 
diff --git a/models/openconfig/compile-yangs.sh b/models/openconfig/compile-yangs.sh
new file mode 100755
index 0000000..1d65364
--- /dev/null
+++ b/models/openconfig/compile-yangs.sh
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+
+YANG_ROOT=$1
+
+CONFDC_ARGS=' -c'
+
+# YANGPATHS
+for path in $(find $YANG_ROOT -type d); do
+  CONFDC_ARGS+=" --yangpath $path"
+done
+
+# create output dir
+mkdir -p fxs
+
+# compile .yang s
+for yang in $(find $YANG_ROOT -type f -name '*.yang'); do
+  BASE=$(basename $yang)
+  OUT="${BASE%.yang}.fxs"
+  echo "Compiling..$yang"
+  confdc $CONFDC_ARGS -o fxs/$OUT -- $yang
+done
diff --git a/models/openconfig/pom.xml b/models/openconfig/pom.xml
index 008311a..8737789 100644
--- a/models/openconfig/pom.xml
+++ b/models/openconfig/pom.xml
@@ -117,6 +117,17 @@
                 </executions>
             </plugin>
 
+            <!-- FIXME: YANG tool generates problematic code -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <compilerArgs>
+                      <arg>-XepDisableAllChecks</arg>
+                      <arg>-Xep:BetaApi:OFF</arg>
+                    </compilerArgs>
+                </configuration>
+            </plugin>
 
         </plugins>
 
diff --git a/modules.defs b/modules.defs
index 2ae8cfd..b090666 100644
--- a/modules.defs
+++ b/modules.defs
@@ -108,6 +108,7 @@
     '//drivers/barefoot:onos-drivers-barefoot-oar',
     '//drivers/hp:onos-drivers-hp-oar',
     '//drivers/p4runtime:onos-drivers-p4runtime-oar',
+    '//drivers/gnmi:onos-drivers-gnmi-oar',
     '//drivers/polatis/netconf:onos-drivers-polatis-netconf-oar',
 ]
 
diff --git a/pipelines/fabric/src/main/resources/Makefile b/pipelines/fabric/src/main/resources/Makefile
index f11de1b..e82fd31 100644
--- a/pipelines/fabric/src/main/resources/Makefile
+++ b/pipelines/fabric/src/main/resources/Makefile
@@ -12,6 +12,12 @@
 	mv p4c-out/bmv2/fabric.p4rt p4c-out/bmv2/fabric.p4info
 	rm -f p4c-out/bmv2/fabric.p4i
 
+bmv2-spgw:
+	p4c-bm2-ss -o p4c-out/bmv2/fabric-spgw.json \
+	        $(BMV2_OPTIONS) -DWITH_SPGW \
+    		--p4runtime-file p4c-out/bmv2/fabric-spgw.p4info \
+    		--p4runtime-format text fabric.p4
+
 custom:
 	p4c -v -x p4-16 -b $(BACKEND) \
 		$(BACKEND_OPTIONS) -o p4c-out/$(BACKEND) \
diff --git a/pipelines/fabric/src/main/resources/fabric.p4 b/pipelines/fabric/src/main/resources/fabric.p4
index d27ed87..56ab6e9 100644
--- a/pipelines/fabric/src/main/resources/fabric.p4
+++ b/pipelines/fabric/src/main/resources/fabric.p4
@@ -26,6 +26,10 @@
 #include "include/checksum.p4"
 #include "include/parser.p4"
 
+#ifdef WITH_SPGW
+#include "include/spgw.p4"
+#endif // WITH_SPGW
+
 control FabricIngress (
 inout parsed_headers_t hdr,
 inout fabric_metadata_t fabric_metadata,
@@ -35,13 +39,23 @@
     Forwarding() forwarding;
     Next() next;
     PortCountersControl() port_counters_control;
+    EgressNextControl() egress_next;
 
     apply {
         packet_io_ingress.apply(hdr, fabric_metadata, standard_metadata);
+#ifdef WITH_SPGW
+#ifdef WITH_SPGW_PCC_GATING
+        fabric_metadata.spgw.l4_src_port = fabric_metadata.l4_src_port;
+        fabric_metadata.spgw.l4_dst_port = fabric_metadata.l4_dst_port;
+#endif // WITH_SPGW_PCC_GATING
+        spgw_ingress.apply(hdr.gtpu_ipv4, hdr.gtpu_udp, hdr.gtpu,
+                           hdr.ipv4, hdr.udp, fabric_metadata.spgw);
+#endif // WITH_SPGW
         filtering.apply(hdr, fabric_metadata, standard_metadata);
         forwarding.apply(hdr, fabric_metadata, standard_metadata);
         next.apply(hdr, fabric_metadata, standard_metadata);
         port_counters_control.apply(hdr, fabric_metadata, standard_metadata);
+        egress_next.apply(hdr, fabric_metadata, standard_metadata);
     }
 }
 
@@ -49,10 +63,12 @@
                       inout fabric_metadata_t fabric_metadata,
                       inout standard_metadata_t standard_metadata) {
     PacketIoEgress() pkt_io_egress;
-    EgressNextControl() egress_next;
     apply {
-        egress_next.apply(hdr, fabric_metadata, standard_metadata);
         pkt_io_egress.apply(hdr, fabric_metadata, standard_metadata);
+#ifdef WITH_SPGW
+        spgw_egress.apply(hdr.gtpu_ipv4, hdr.gtpu_udp, hdr.gtpu,
+                          fabric_metadata.spgw, standard_metadata);
+#endif // WITH_SPGW
     }
 }
 
diff --git a/pipelines/fabric/src/main/resources/include/checksum.p4 b/pipelines/fabric/src/main/resources/include/checksum.p4
index dfcadb6..d920235 100644
--- a/pipelines/fabric/src/main/resources/include/checksum.p4
+++ b/pipelines/fabric/src/main/resources/include/checksum.p4
@@ -17,11 +17,14 @@
 #ifndef __CHECKSUM__
 #define __CHECKSUM__
 
+#ifdef WITH_SPGW
+#include "spgw.p4"
+#endif // WITH_SPGW
+
 control FabricComputeChecksum(inout parsed_headers_t hdr,
                               inout fabric_metadata_t meta)
 {
     apply {
-#ifdef TARGET_BMV2
         update_checksum(hdr.ipv4.isValid(),
             {
                 hdr.ipv4.version,
@@ -39,7 +42,10 @@
             hdr.ipv4.hdr_checksum,
             HashAlgorithm.csum16
         );
-#endif
+#ifdef WITH_SPGW
+        update_gtpu_checksum.apply(hdr.gtpu_ipv4, hdr.gtpu_udp, hdr.gtpu,
+                                   hdr.ipv4, hdr.udp);
+#endif // WITH_SPGW
     }
 }
 
@@ -47,7 +53,6 @@
                              inout fabric_metadata_t meta)
 {
     apply {
-#ifdef TARGET_BMV2
         verify_checksum(hdr.ipv4.isValid(),
             {
                 hdr.ipv4.version,
@@ -65,7 +70,9 @@
             hdr.ipv4.hdr_checksum,
             HashAlgorithm.csum16
         );
-#endif
+#ifdef WITH_SPGW
+        verify_gtpu_checksum.apply(hdr.gtpu_ipv4);
+#endif // WITH_SPGW
     }
 }
 
diff --git a/pipelines/fabric/src/main/resources/include/control/filtering.p4 b/pipelines/fabric/src/main/resources/include/control/filtering.p4
index 5a93e5b..58bf147 100644
--- a/pipelines/fabric/src/main/resources/include/control/filtering.p4
+++ b/pipelines/fabric/src/main/resources/include/control/filtering.p4
@@ -24,6 +24,8 @@
     inout parsed_headers_t hdr,
     inout fabric_metadata_t fabric_metadata,
     inout standard_metadata_t standard_metadata) {
+    direct_counter(CounterType.packets_and_bytes) ingress_port_vlan_counter;
+    direct_counter(CounterType.packets_and_bytes) fwd_classifier_counter;
 
     action drop() {
         mark_to_drop();
@@ -65,7 +67,9 @@
             nop;
             drop;
         }
-        const default_action = drop();
+
+        const default_action = nop();
+        counters = ingress_port_vlan_counter;
     }
 
     // Originally TMAC table in OF-DPA pipeline
@@ -81,6 +85,7 @@
         }
 
         const default_action = set_forwarding_type(FWD_BRIDGING);
+        counters = fwd_classifier_counter;
     }
 
     apply {
diff --git a/pipelines/fabric/src/main/resources/include/control/forwarding.p4 b/pipelines/fabric/src/main/resources/include/control/forwarding.p4
index 4e9ada1..de46115 100644
--- a/pipelines/fabric/src/main/resources/include/control/forwarding.p4
+++ b/pipelines/fabric/src/main/resources/include/control/forwarding.p4
@@ -27,6 +27,12 @@
     inout fabric_metadata_t fabric_metadata,
     inout standard_metadata_t standard_metadata) {
 
+    direct_counter(CounterType.packets_and_bytes) bridging_counter;
+    direct_counter(CounterType.packets_and_bytes) mpls_counter;
+    direct_counter(CounterType.packets_and_bytes) unicast_v4_counter;
+    direct_counter(CounterType.packets_and_bytes) multicast_v4_counter;
+    direct_counter(CounterType.packets_and_bytes) acl_counter;
+
     action drop() {
         mark_to_drop();
     }
@@ -53,6 +59,7 @@
         actions = {
             set_next_id;
         }
+        counters = bridging_counter;
     }
 
     table mpls {
@@ -63,6 +70,7 @@
         actions = {
             pop_mpls_and_next;
         }
+        counters = mpls_counter;
     }
 
     table unicast_v4 {
@@ -73,6 +81,7 @@
         actions = {
             set_next_id;
         }
+        counters = unicast_v4_counter;
     }
 
     table multicast_v4 {
@@ -84,8 +93,13 @@
         actions = {
             set_next_id;
         }
+        counters = multicast_v4_counter;
     }
 
+#ifdef WITH_IPV6
+    direct_counter(CounterType.packets_and_bytes) unicast_v6_counter;
+    direct_counter(CounterType.packets_and_bytes) multicast_v6_counter;
+
     table unicast_v6 {
         key = {
             hdr.ipv6.dst_addr: lpm;
@@ -94,6 +108,7 @@
         actions = {
             set_next_id;
         }
+        counters = unicast_v6_counter;
     }
 
     table multicast_v6 {
@@ -105,7 +120,9 @@
         actions = {
             set_next_id;
         }
+        counters = multicast_v6_counter;
     }
+#endif // WITH_IPV6
 
     table acl {
         key = {
@@ -133,24 +150,24 @@
 
         const default_action = nop();
         size = 256;
+        counters = acl_counter;
     }
 
     apply {
         if(fabric_metadata.fwd_type == FWD_BRIDGING) bridging.apply();
         else if (fabric_metadata.fwd_type == FWD_MPLS) {
             mpls.apply();
-            if (hdr.ipv4.isValid()) {
-                hdr.ethernet.ether_type = ETHERTYPE_IPV4;
-                fabric_metadata.original_ether_type = ETHERTYPE_IPV4;
-            } else {
-                hdr.ethernet.ether_type = ETHERTYPE_IPV6;
-                fabric_metadata.original_ether_type = ETHERTYPE_IPV6;
-            }
+
+            // TODO: IPv6
+            hdr.vlan_tag.ether_type = ETHERTYPE_IPV4;
+            fabric_metadata.original_ether_type = ETHERTYPE_IPV4;
         }
         else if (fabric_metadata.fwd_type == FWD_IPV4_UNICAST) unicast_v4.apply();
         else if (fabric_metadata.fwd_type == FWD_IPV4_MULTICAST) multicast_v4.apply();
+#ifdef WITH_IPV6
         else if (fabric_metadata.fwd_type == FWD_IPV6_UNICAST) unicast_v6.apply();
         else if (fabric_metadata.fwd_type == FWD_IPV6_MULTICAST) multicast_v6.apply();
+#endif // WITH_IPV6
         acl.apply();
     }
 }
diff --git a/pipelines/fabric/src/main/resources/include/control/next.p4 b/pipelines/fabric/src/main/resources/include/control/next.p4
index a58e88e..e0ebbe8 100644
--- a/pipelines/fabric/src/main/resources/include/control/next.p4
+++ b/pipelines/fabric/src/main/resources/include/control/next.p4
@@ -25,6 +25,9 @@
     inout fabric_metadata_t fabric_metadata,
     inout standard_metadata_t standard_metadata) {
     action_selector(HashAlgorithm.crc16, 32w64, 32w16) ecmp_selector;
+    direct_counter(CounterType.packets_and_bytes) simple_counter;
+    direct_counter(CounterType.packets_and_bytes) hashed_counter;
+    direct_counter(CounterType.packets_and_bytes) broadcast_counter;
 
     action output(port_num_t port_num) {
         standard_metadata.egress_spec = port_num;
@@ -60,7 +63,7 @@
     action push_mpls (mpls_label_t label, bit<3> tc) {
         // Suppose that the maximum number of label is one.
         hdr.mpls.setValid();
-        hdr.ethernet.ether_type = ETHERTYPE_MPLS;
+        hdr.vlan_tag.ether_type = ETHERTYPE_MPLS;
         hdr.mpls.label = label;
         hdr.mpls.tc = tc;
         hdr.mpls.bos = 1w1; // BOS = TRUE
@@ -92,7 +95,9 @@
             output;
             set_vlan_output;
             l3_routing;
+            mpls_routing_v4;
         }
+        counters = simple_counter;
     }
 
     table hashed {
@@ -112,6 +117,7 @@
         }
 
         implementation = ecmp_selector;
+        counters = hashed_counter;
     }
 
     /*
@@ -124,6 +130,7 @@
         actions = {
             set_mcast_group;
         }
+        counters = broadcast_counter;
     }
 
     apply {
@@ -132,9 +139,11 @@
                 if(hdr.ipv4.isValid()) {
                     hdr.ipv4.ttl = hdr.ipv4.ttl - 1;
                 }
+#ifdef WITH_IPV6
                 else if (hdr.ipv6.isValid()) {
                     hdr.ipv6.hop_limit = hdr.ipv6.hop_limit - 1;
                 }
+#endif // WITH_IPV6
             }
         }
         hashed.apply();
@@ -150,11 +159,7 @@
     apply {
         // pop internal vlan if the meta is set
         if (fabric_metadata.pop_vlan_at_egress) {
-            if (hdr.mpls.isValid()) {
-                hdr.ethernet.ether_type = ETHERTYPE_MPLS;
-            } else {
-                hdr.ethernet.ether_type = fabric_metadata.original_ether_type;
-            }
+            hdr.ethernet.ether_type = hdr.vlan_tag.ether_type;
             hdr.vlan_tag.setInvalid();
         }
     }
diff --git a/pipelines/fabric/src/main/resources/include/define.p4 b/pipelines/fabric/src/main/resources/include/define.p4
index 4765824..f4e5b56 100644
--- a/pipelines/fabric/src/main/resources/include/define.p4
+++ b/pipelines/fabric/src/main/resources/include/define.p4
@@ -44,6 +44,8 @@
 const bit<8> PROTO_UDP = 17;
 const bit<8> PROTO_ICMPV6 = 58;
 
+const bit<4> IPV4_MIN_IHL = 5;
+
 #ifndef CPU_PORT
 const port_num_t CPU_PORT = 255;
 #endif
@@ -56,5 +58,30 @@
 const fwd_type_t FWD_IPV6_MULTICAST = 5;
 
 const bit<8> DEFAULT_MPLS_TTL = 64;
+const bit<8> DEFAULT_IPV4_TTL = 64;
+
+#define ETH_HDR_SIZE 14
+#define IPV4_HDR_SIZE 20
+#define UDP_HDR_SIZE 8
+#define GTP_HDR_SIZE 8
+
+#define UDP_PORT_GTPU 2152
+#define GTP_GPDU 0xff
+#define GTPU_VERSION 0x01
+#define GTP_PROTOCOL_TYPE_GTP 0x01
+
+typedef bit direction_t;
+typedef bit pcc_gate_status_t;
+typedef bit<32> sdf_rule_id_t;
+typedef bit<32> pcc_rule_id_t;
+
+const sdf_rule_id_t DEFAULT_SDF_RULE_ID = 0;
+const pcc_rule_id_t DEFAULT_PCC_RULE_ID = 0;
+
+const direction_t DIR_UPLINK = 1w0;
+const direction_t DIR_DOWNLINK = 1w1;
+
+const pcc_gate_status_t PCC_GATE_OPEN = 1w0;
+const pcc_gate_status_t PCC_GATE_CLOSED = 1w1;
 
 #endif
diff --git a/pipelines/fabric/src/main/resources/include/header.p4 b/pipelines/fabric/src/main/resources/include/header.p4
index df9958b..0a0814e 100644
--- a/pipelines/fabric/src/main/resources/include/header.p4
+++ b/pipelines/fabric/src/main/resources/include/header.p4
@@ -115,6 +115,36 @@
     bit<64> timestamp;
 }
 
+#ifdef WITH_SPGW
+// GTPU v1
+header gtpu_t {
+    bit<3>  version;    /* version */
+    bit<1>  pt;         /* protocol type */
+    bit<1>  spare;      /* reserved */
+    bit<1>  ex_flag;    /* next extension hdr present? */
+    bit<1>  seq_flag;   /* sequence no. */
+    bit<1>  npdu_flag;  /* n-pdn number present ? */
+    bit<8>  msgtype;    /* message type */
+    bit<16> msglen;     /* message length */
+    bit<32> teid;       /* tunnel endpoint id */
+}
+
+struct spgw_meta_t {
+    bool              do_spgw;
+    direction_t       direction;
+    bit<32>           teid;
+    bit<32>           s1u_enb_addr;
+    bit<32>           s1u_sgw_addr;
+#ifdef WITH_SPGW_PCC_GATING
+    bit<16>           l4_src_port;
+    bit<16>           l4_dst_port;
+    pcc_gate_status_t pcc_gate_status;
+    sdf_rule_id_t     sdf_rule_id;
+    pcc_rule_id_t     pcc_rule_id;
+#endif // WITH_SPGW_PCC_GATING
+}
+#endif // WITH_SPGW
+
 //Custom metadata definition
 struct fabric_metadata_t {
     fwd_type_t fwd_type;
@@ -124,15 +154,24 @@
     bit<16> l4_src_port;
     bit<16> l4_dst_port;
     bit<16> original_ether_type;
+#ifdef WITH_SPGW
+    spgw_meta_t spgw;
+#endif // WITH_SPGW
 }
 
 struct parsed_headers_t {
     ethernet_t ethernet;
     vlan_tag_t vlan_tag;
-    vlan_tag_t inner_vlan_tag;
     mpls_t mpls;
+#ifdef WITH_SPGW
+    ipv4_t gtpu_ipv4;
+    udp_t gtpu_udp;
+    gtpu_t gtpu;
+#endif // WITH_SPGW
     ipv4_t ipv4;
+#ifdef WITH_IPV6
     ipv6_t ipv6;
+#endif // WITH_IPV6
     arp_t arp;
     tcp_t tcp;
     udp_t udp;
diff --git a/pipelines/fabric/src/main/resources/include/parser.p4 b/pipelines/fabric/src/main/resources/include/parser.p4
index b5eadbc..f4b3252 100644
--- a/pipelines/fabric/src/main/resources/include/parser.p4
+++ b/pipelines/fabric/src/main/resources/include/parser.p4
@@ -41,13 +41,13 @@
         packet.extract(hdr.ethernet);
         fabric_metadata.original_ether_type = hdr.ethernet.ether_type;
         transition select(hdr.ethernet.ether_type){
-            ETHERTYPE_QINQ_NON_STD: parse_vlan_tag;
-            ETHERTYPE_QINQ: parse_vlan_tag;
             ETHERTYPE_VLAN: parse_vlan_tag;
             ETHERTYPE_MPLS: parse_mpls;
             ETHERTYPE_ARP: parse_arp;
             ETHERTYPE_IPV4: parse_ipv4;
+#ifdef WITH_IPV6
             ETHERTYPE_IPV6: parse_ipv6;
+#endif // WITH_IPV6
             default: accept;
         }
     }
@@ -55,20 +55,12 @@
     state parse_vlan_tag {
         packet.extract(hdr.vlan_tag);
         transition select(hdr.vlan_tag.ether_type){
-            ETHERTYPE_VLAN: parse_inner_vlan_tag;
             ETHERTYPE_ARP: parse_arp;
             ETHERTYPE_IPV4: parse_ipv4;
+#ifdef WITH_IPV6
             ETHERTYPE_IPV6: parse_ipv6;
-            default: accept;
-        }
-    }
-
-    state parse_inner_vlan_tag {
-        packet.extract(hdr.inner_vlan_tag);
-        transition select(hdr.vlan_tag.ether_type){
-            ETHERTYPE_ARP: parse_arp;
-            ETHERTYPE_IPV4: parse_ipv4;
-            ETHERTYPE_IPV6: parse_ipv6;
+#endif // WITH_IPV6
+            ETHERTYPE_MPLS: parse_mpls;
             default: accept;
         }
     }
@@ -81,7 +73,9 @@
         transition select(packet.lookahead<bit<4>>()) {
             //The packet should be either IPv4 or IPv6.
             IP_VERSION_4: parse_ipv4;
+#ifdef WITH_IPV6
             IP_VERSION_6: parse_ipv6;
+#endif // WITH_IPV6
             default: parse_ethernet;
         }
     }
@@ -98,6 +92,7 @@
         }
     }
 
+#ifdef WITH_IPV6
     state parse_ipv6 {
         packet.extract(hdr.ipv6);
         fabric_metadata.ip_proto = hdr.ipv6.next_hdr;
@@ -108,6 +103,7 @@
             default: accept;
         }
     }
+#endif // WITH_IPV6
 
     state parse_arp {
         packet.extract(hdr.arp);
@@ -125,13 +121,44 @@
         packet.extract(hdr.udp);
         fabric_metadata.l4_src_port = hdr.udp.src_port;
         fabric_metadata.l4_dst_port = hdr.udp.dst_port;
+#ifdef WITH_SPGW
+        transition select(hdr.udp.dst_port) {
+            UDP_PORT_GTPU: parse_gtpu;
+            default: accept;
+        }
+#else
         transition accept;
+#endif // WITH_SPGW
     }
 
     state parse_icmp {
         packet.extract(hdr.icmp);
         transition accept;
     }
+
+#ifdef WITH_SPGW
+    state parse_gtpu {
+        packet.extract(hdr.gtpu);
+        transition parse_ipv4_inner;
+    }
+
+    state parse_ipv4_inner {
+        packet.extract(hdr.gtpu_ipv4);
+        transition select(hdr.gtpu_ipv4.protocol) {
+            PROTO_TCP: parse_tcp;
+            PROTO_UDP: parse_udp_inner;
+            PROTO_ICMP: parse_icmp;
+            default: accept;
+        }
+    }
+
+    state parse_udp_inner {
+        packet.extract(hdr.gtpu_udp);
+        fabric_metadata.l4_src_port = hdr.gtpu_udp.src_port;
+        fabric_metadata.l4_dst_port = hdr.gtpu_udp.dst_port;
+        transition accept;
+    }
+#endif // WITH_SPGW
 }
 
 control FabricDeparser(packet_out packet, in parsed_headers_t hdr) {
@@ -139,11 +166,17 @@
         packet.emit(hdr.packet_in);
         packet.emit(hdr.ethernet);
         packet.emit(hdr.vlan_tag);
-        packet.emit(hdr.inner_vlan_tag);
         packet.emit(hdr.mpls);
         packet.emit(hdr.arp);
+#ifdef WITH_SPGW
+        packet.emit(hdr.gtpu_ipv4);
+        packet.emit(hdr.gtpu_udp);
+        packet.emit(hdr.gtpu);
+#endif // WITH_SPGW
         packet.emit(hdr.ipv4);
+#ifdef WITH_IPV6
         packet.emit(hdr.ipv6);
+#endif // WITH_IPV6
         packet.emit(hdr.tcp);
         packet.emit(hdr.udp);
         packet.emit(hdr.icmp);
diff --git a/pipelines/fabric/src/main/resources/include/spgw.p4 b/pipelines/fabric/src/main/resources/include/spgw.p4
new file mode 100644
index 0000000..3edca18
--- /dev/null
+++ b/pipelines/fabric/src/main/resources/include/spgw.p4
@@ -0,0 +1,333 @@
+/*
+ * 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.
+ */
+
+#ifndef __SPGW__
+#define __SPGW__
+
+
+control spgw_ingress(
+        inout ipv4_t      gtpu_ipv4,
+        inout udp_t       gtpu_udp,
+        inout gtpu_t      gtpu,
+        inout ipv4_t      ipv4,
+        inout udp_t       udp,
+        inout spgw_meta_t spgw_meta
+    ) {
+
+    direct_counter(CounterType.packets_and_bytes) ue_counter;
+
+    action drop_now() {
+        mark_to_drop();
+        exit;
+    }
+
+    action gtpu_decap() {
+        gtpu_ipv4.setInvalid();
+        gtpu_udp.setInvalid();
+        gtpu.setInvalid();
+    }
+
+    action set_dl_sess_info(bit<32> teid,
+                            bit<32> s1u_enb_addr,
+                            bit<32> s1u_sgw_addr) {
+        spgw_meta.teid = teid;
+        spgw_meta.s1u_enb_addr = s1u_enb_addr;
+        spgw_meta.s1u_sgw_addr = s1u_sgw_addr;
+    }
+
+    action update_ue_cdr() {
+        ue_counter.count();
+    }
+
+    table ue_filter_table {
+        key = {
+            // IP prefixes of the UEs managed by this switch (when downlink)
+            ipv4.dst_addr : lpm;
+        }
+        actions = {
+            NoAction();
+        }
+    }
+
+    table s1u_filter_table {
+        key = {
+            // IP addresses of the S1U interfaces embodied by this switch.
+            spgw_meta.s1u_sgw_addr : exact;
+        }
+        actions = {
+            NoAction();
+        }
+    }
+
+#ifdef WITH_SPGW_PCC_GATING
+    action set_sdf_rule_id(sdf_rule_id_t id) {
+        spgw_meta.sdf_rule_id = id;
+    }
+
+    action set_pcc_rule_id(pcc_rule_id_t id) {
+        spgw_meta.pcc_rule_id = id;
+    }
+
+    action set_pcc_info(pcc_gate_status_t gate_status) {
+        spgw_meta.pcc_gate_status = gate_status;
+    }
+
+    table sdf_rule_lookup {
+        key = {
+            spgw_meta.direction   : exact;
+            ipv4.src_addr         : ternary;
+            ipv4.dst_addr         : ternary;
+            ipv4.protocol         : ternary;
+            spgw_meta.l4_src_port : ternary;
+            spgw_meta.l4_dst_port : ternary;
+        }
+        actions = {
+            set_sdf_rule_id();
+        }
+        const default_action = set_sdf_rule_id(DEFAULT_SDF_RULE_ID);
+    }
+
+    table pcc_rule_lookup {
+        key = {
+            spgw_meta.sdf_rule_id : exact;
+        }
+        actions = {
+            set_pcc_rule_id();
+        }
+        const default_action = set_pcc_rule_id(DEFAULT_PCC_RULE_ID);
+    }
+
+    table pcc_info_lookup {
+        key = {
+            spgw_meta.pcc_rule_id : exact;
+        }
+        actions = {
+            set_pcc_info();
+        }
+        const default_action = set_pcc_info(PCC_GATE_OPEN);
+    }
+#endif // WITH_SPGW_PCC_GATING
+
+    table dl_sess_lookup {
+        key = {
+            // UE addr for downlink
+            ipv4.dst_addr : exact;
+        }
+        actions = {
+            set_dl_sess_info();
+        }
+    }
+
+    table ue_cdr_table {
+        key = {
+            // UE addr for downlink
+            ipv4.dst_addr : exact;
+        }
+        actions = {
+            update_ue_cdr();
+        }
+        counters = ue_counter;
+    }
+
+    apply {
+        spgw_meta.do_spgw = false;
+        if (gtpu.isValid()) {
+            // If here, the parsed ipv4 header is the outer GTP one, but
+            // fabric needs to forward on the inner one, i.e. this.
+            // We store the outer values we need in the metadata, then replace
+            // the ipv4 header extracted before with this one.
+            spgw_meta.s1u_enb_addr = ipv4.src_addr;
+            spgw_meta.s1u_sgw_addr = ipv4.dst_addr;
+            ipv4 = gtpu_ipv4;
+            udp = gtpu_udp;
+
+            if (s1u_filter_table.apply().hit) {
+                // TODO: check also that gtpu.msgtype == GTP_GPDU
+                spgw_meta.do_spgw = true;
+                spgw_meta.direction = DIR_UPLINK;
+            }
+        } else if (ue_filter_table.apply().hit) {
+            spgw_meta.do_spgw = true;
+            spgw_meta.direction = DIR_DOWNLINK;
+        }
+
+        if (!spgw_meta.do_spgw) {
+            // Exit this control block.
+            return;
+        }
+
+        if (spgw_meta.direction == DIR_UPLINK) {
+            gtpu_decap();
+        }
+
+#ifdef WITH_SPGW_PCC_GATING
+        // Allow all traffic by default.
+        spgw_meta.pcc_gate_status = PCC_GATE_OPEN;
+
+        sdf_rule_lookup.apply();
+        pcc_rule_lookup.apply();
+        pcc_info_lookup.apply();
+
+        if (spgw_meta.pcc_gate_status == PCC_GATE_CLOSED) {
+            drop_now();
+        }
+#endif // WITH_SPGW_PCC_GATING
+
+        if (spgw_meta.direction == DIR_DOWNLINK) {
+            if (!dl_sess_lookup.apply().hit) {
+                // We have no other choice than drop, as we miss the session
+                // info necessary to properly GTPU encap the packet.
+                drop_now();
+            }
+            ue_cdr_table.apply();
+        }
+    }
+}
+
+
+control spgw_egress(
+        out ipv4_t              gtpu_ipv4,
+        out udp_t               gtpu_udp,
+        out gtpu_t              gtpu,
+        in  spgw_meta_t         spgw_meta,
+        in  standard_metadata_t std_meta
+    ) {
+
+    action gtpu_encap() {
+        gtpu_ipv4.setValid();
+        gtpu_ipv4.version = IP_VERSION_4;
+        gtpu_ipv4.ihl = IPV4_MIN_IHL;
+        gtpu_ipv4.diffserv = 0;
+        gtpu_ipv4.total_len = ((bit<16>)std_meta.packet_length
+            - ETH_HDR_SIZE + IPV4_HDR_SIZE + UDP_HDR_SIZE + GTP_HDR_SIZE);
+        gtpu_ipv4.identification = 0x1513; /* From NGIC */
+        gtpu_ipv4.flags = 0;
+        gtpu_ipv4.frag_offset = 0;
+        gtpu_ipv4.ttl = DEFAULT_IPV4_TTL;
+        gtpu_ipv4.protocol = PROTO_UDP;
+        gtpu_ipv4.dst_addr = spgw_meta.s1u_enb_addr;
+        gtpu_ipv4.src_addr = spgw_meta.s1u_sgw_addr;
+        gtpu_ipv4.hdr_checksum = 0; // Updated later
+
+        gtpu_udp.setValid();
+        gtpu_udp.src_port = UDP_PORT_GTPU;
+        gtpu_udp.dst_port = UDP_PORT_GTPU;
+        gtpu_udp.len = ((bit<16>)std_meta.packet_length
+            - ETH_HDR_SIZE + UDP_HDR_SIZE + GTP_HDR_SIZE);
+        gtpu_udp.checksum = 0; // Updated later
+
+        gtpu.setValid();
+        gtpu.version = GTPU_VERSION;
+        gtpu.pt = GTP_PROTOCOL_TYPE_GTP;
+        gtpu.spare = 0;
+        gtpu.ex_flag = 0;
+        gtpu.seq_flag = 0;
+        gtpu.npdu_flag = 0;
+        gtpu.msgtype = GTP_GPDU;
+        gtpu.msglen = ((bit<16>)std_meta.packet_length - ETH_HDR_SIZE);
+        gtpu.teid = spgw_meta.teid;
+    }
+
+    apply {
+        if (spgw_meta.do_spgw && spgw_meta.direction == DIR_DOWNLINK) {
+            gtpu_encap();
+        }
+    }
+}
+
+
+control verify_gtpu_checksum(
+        inout ipv4_t gtpu_ipv4
+    ) {
+    apply {
+        // TODO: re-enable gtpu_ipv4 verification
+        // with the current parser logic, gtpu_ip4 contains values of
+        // the inner header, which is already verified by include/checksum.p4.
+        // We need to modify the parser to copy the outer header somewhere
+        // else, and verify that here.
+
+        // verify_checksum(gtpu_ipv4.isValid(),
+        //     {
+        //         gtpu_ipv4.version,
+        //         gtpu_ipv4.ihl,
+        //         gtpu_ipv4.diffserv,
+        //         gtpu_ipv4.total_len,
+        //         gtpu_ipv4.identification,
+        //         gtpu_ipv4.flags,
+        //         gtpu_ipv4.frag_offset,
+        //         gtpu_ipv4.ttl,
+        //         gtpu_ipv4.protocol,
+        //         gtpu_ipv4.src_addr,
+        //         gtpu_ipv4.dst_addr
+        //     },
+        //     gtpu_ipv4.hdr_checksum,
+        //     HashAlgorithm.csum16
+        // );
+    }
+}
+
+
+control update_gtpu_checksum(
+        inout ipv4_t gtpu_ipv4,
+        inout udp_t  gtpu_udp,
+        in    gtpu_t gtpu,
+        in    ipv4_t ipv4,
+        in    udp_t  udp
+    ) {
+    apply {
+        // Compute outer IPv4 checksum.
+        update_checksum(gtpu_ipv4.isValid(),
+            {
+                gtpu_ipv4.version,
+                gtpu_ipv4.ihl,
+                gtpu_ipv4.diffserv,
+                gtpu_ipv4.total_len,
+                gtpu_ipv4.identification,
+                gtpu_ipv4.flags,
+                gtpu_ipv4.frag_offset,
+                gtpu_ipv4.ttl,
+                gtpu_ipv4.protocol,
+                gtpu_ipv4.src_addr,
+                gtpu_ipv4.dst_addr
+            },
+            gtpu_ipv4.hdr_checksum,
+            HashAlgorithm.csum16
+        );
+
+        // Compute outer UDP checksum.
+        update_checksum_with_payload(gtpu_udp.isValid(),
+            {
+                gtpu_ipv4.src_addr,
+                gtpu_ipv4.dst_addr,
+                8w0,
+                gtpu_ipv4.protocol,
+                gtpu_udp.len,
+                gtpu_udp.src_port,
+                gtpu_udp.dst_port,
+                gtpu_udp.len,
+                gtpu,
+                ipv4,
+                // FIXME: we are assuming only UDP for downlink packets
+                // How to conditionally switch between UDP/TCP/ICMP?
+                udp
+            },
+            gtpu_udp.checksum,
+            HashAlgorithm.csum16
+        );
+    }
+}
+
+#endif
diff --git a/pipelines/fabric/src/main/resources/p4c-out/bmv2/fabric-spgw.json b/pipelines/fabric/src/main/resources/p4c-out/bmv2/fabric-spgw.json
new file mode 100644
index 0000000..81b13e5
--- /dev/null
+++ b/pipelines/fabric/src/main/resources/p4c-out/bmv2/fabric-spgw.json
@@ -0,0 +1,6244 @@
+{
+  "program" : "fabric.p4",
+  "__meta__" : {
+    "version" : [2, 7],
+    "compiler" : "https://github.com/p4lang/p4c"
+  },
+  "header_types" : [
+    {
+      "name" : "scalars_0",
+      "id" : 0,
+      "fields" : [
+        ["tmp", 4, false],
+        ["tmp_0", 32, false],
+        ["tmp_1", 32, false],
+        ["spgw_ingress_tmp_2", 1, false],
+        ["spgw_ingress_tmp_3", 1, false],
+        ["spgw_ingress_tmp_4", 1, false],
+        ["next_tmp_0", 1, false],
+        ["spgw_ingress_hasReturned_0", 1, false],
+        ["fabric_metadata_t.fwd_type", 3, false],
+        ["fabric_metadata_t.next_id", 32, false],
+        ["fabric_metadata_t.pop_vlan_at_egress", 1, false],
+        ["fabric_metadata_t.ip_proto", 8, false],
+        ["fabric_metadata_t.l4_src_port", 16, false],
+        ["fabric_metadata_t.l4_dst_port", 16, false],
+        ["fabric_metadata_t.original_ether_type", 16, false],
+        ["_padding_1", 3, false]
+      ]
+    },
+    {
+      "name" : "ethernet_t",
+      "id" : 1,
+      "fields" : [
+        ["dst_addr", 48, false],
+        ["src_addr", 48, false],
+        ["ether_type", 16, false]
+      ]
+    },
+    {
+      "name" : "vlan_tag_t",
+      "id" : 2,
+      "fields" : [
+        ["pri", 3, false],
+        ["cfi", 1, false],
+        ["vlan_id", 12, false],
+        ["ether_type", 16, false]
+      ]
+    },
+    {
+      "name" : "mpls_t",
+      "id" : 3,
+      "fields" : [
+        ["label", 20, false],
+        ["tc", 3, false],
+        ["bos", 1, false],
+        ["ttl", 8, false]
+      ]
+    },
+    {
+      "name" : "ipv4_t",
+      "id" : 4,
+      "fields" : [
+        ["version", 4, false],
+        ["ihl", 4, false],
+        ["diffserv", 8, false],
+        ["total_len", 16, false],
+        ["identification", 16, false],
+        ["flags", 3, false],
+        ["frag_offset", 13, false],
+        ["ttl", 8, false],
+        ["protocol", 8, false],
+        ["hdr_checksum", 16, false],
+        ["src_addr", 32, false],
+        ["dst_addr", 32, false]
+      ]
+    },
+    {
+      "name" : "udp_t",
+      "id" : 5,
+      "fields" : [
+        ["src_port", 16, false],
+        ["dst_port", 16, false],
+        ["len", 16, false],
+        ["checksum", 16, false]
+      ]
+    },
+    {
+      "name" : "gtpu_t",
+      "id" : 6,
+      "fields" : [
+        ["version", 3, false],
+        ["pt", 1, false],
+        ["spare", 1, false],
+        ["ex_flag", 1, false],
+        ["seq_flag", 1, false],
+        ["npdu_flag", 1, false],
+        ["msgtype", 8, false],
+        ["msglen", 16, false],
+        ["teid", 32, false]
+      ]
+    },
+    {
+      "name" : "arp_t",
+      "id" : 7,
+      "fields" : [
+        ["hw_type", 16, false],
+        ["proto_type", 16, false],
+        ["hw_addr_len", 8, false],
+        ["proto_addr_len", 8, false],
+        ["opcode", 16, false]
+      ]
+    },
+    {
+      "name" : "tcp_t",
+      "id" : 8,
+      "fields" : [
+        ["src_port", 16, false],
+        ["dst_port", 16, false],
+        ["seq_no", 32, false],
+        ["ack_no", 32, false],
+        ["data_offset", 4, false],
+        ["res", 3, false],
+        ["ecn", 3, false],
+        ["ctrl", 6, false],
+        ["window", 16, false],
+        ["checksum", 16, false],
+        ["urgent_ptr", 16, false]
+      ]
+    },
+    {
+      "name" : "icmp_t",
+      "id" : 9,
+      "fields" : [
+        ["icmp_type", 8, false],
+        ["icmp_code", 8, false],
+        ["checksum", 16, false],
+        ["identifier", 16, false],
+        ["sequence_number", 16, false],
+        ["timestamp", 64, false]
+      ]
+    },
+    {
+      "name" : "packet_out_header_t",
+      "id" : 10,
+      "fields" : [
+        ["egress_port", 9, false],
+        ["_pad", 7, false]
+      ]
+    },
+    {
+      "name" : "packet_in_header_t",
+      "id" : 11,
+      "fields" : [
+        ["ingress_port", 9, false],
+        ["_pad", 7, false]
+      ]
+    },
+    {
+      "name" : "spgw_meta_t",
+      "id" : 12,
+      "fields" : [
+        ["do_spgw", 1, 0],
+        ["direction", 1, false],
+        ["teid", 32, false],
+        ["s1u_enb_addr", 32, false],
+        ["s1u_sgw_addr", 32, false],
+        ["_padding", 6, false]
+      ]
+    },
+    {
+      "name" : "standard_metadata",
+      "id" : 13,
+      "fields" : [
+        ["ingress_port", 9, false],
+        ["egress_spec", 9, false],
+        ["egress_port", 9, false],
+        ["clone_spec", 32, false],
+        ["instance_type", 32, false],
+        ["drop", 1, false],
+        ["recirculate_port", 16, false],
+        ["packet_length", 32, false],
+        ["enq_timestamp", 32, false],
+        ["enq_qdepth", 19, false],
+        ["deq_timedelta", 32, false],
+        ["deq_qdepth", 19, false],
+        ["ingress_global_timestamp", 48, false],
+        ["lf_field_list", 32, false],
+        ["mcast_grp", 16, false],
+        ["resubmit_flag", 1, false],
+        ["egress_rid", 16, false],
+        ["checksum_error", 1, false],
+        ["_padding_0", 4, false]
+      ]
+    }
+  ],
+  "headers" : [
+    {
+      "name" : "scalars",
+      "id" : 0,
+      "header_type" : "scalars_0",
+      "metadata" : true,
+      "pi_omit" : true
+    },
+    {
+      "name" : "standard_metadata",
+      "id" : 1,
+      "header_type" : "standard_metadata",
+      "metadata" : true,
+      "pi_omit" : true
+    },
+    {
+      "name" : "ethernet",
+      "id" : 2,
+      "header_type" : "ethernet_t",
+      "metadata" : false,
+      "pi_omit" : true
+    },
+    {
+      "name" : "vlan_tag",
+      "id" : 3,
+      "header_type" : "vlan_tag_t",
+      "metadata" : false,
+      "pi_omit" : true
+    },
+    {
+      "name" : "mpls",
+      "id" : 4,
+      "header_type" : "mpls_t",
+      "metadata" : false,
+      "pi_omit" : true
+    },
+    {
+      "name" : "gtpu_ipv4",
+      "id" : 5,
+      "header_type" : "ipv4_t",
+      "metadata" : false,
+      "pi_omit" : true
+    },
+    {
+      "name" : "gtpu_udp",
+      "id" : 6,
+      "header_type" : "udp_t",
+      "metadata" : false,
+      "pi_omit" : true
+    },
+    {
+      "name" : "gtpu",
+      "id" : 7,
+      "header_type" : "gtpu_t",
+      "metadata" : false,
+      "pi_omit" : true
+    },
+    {
+      "name" : "ipv4",
+      "id" : 8,
+      "header_type" : "ipv4_t",
+      "metadata" : false,
+      "pi_omit" : true
+    },
+    {
+      "name" : "arp",
+      "id" : 9,
+      "header_type" : "arp_t",
+      "metadata" : false,
+      "pi_omit" : true
+    },
+    {
+      "name" : "tcp",
+      "id" : 10,
+      "header_type" : "tcp_t",
+      "metadata" : false,
+      "pi_omit" : true
+    },
+    {
+      "name" : "udp",
+      "id" : 11,
+      "header_type" : "udp_t",
+      "metadata" : false,
+      "pi_omit" : true
+    },
+    {
+      "name" : "icmp",
+      "id" : 12,
+      "header_type" : "icmp_t",
+      "metadata" : false,
+      "pi_omit" : true
+    },
+    {
+      "name" : "packet_out",
+      "id" : 13,
+      "header_type" : "packet_out_header_t",
+      "metadata" : false,
+      "pi_omit" : true
+    },
+    {
+      "name" : "packet_in",
+      "id" : 14,
+      "header_type" : "packet_in_header_t",
+      "metadata" : false,
+      "pi_omit" : true
+    },
+    {
+      "name" : "spgw",
+      "id" : 15,
+      "header_type" : "spgw_meta_t",
+      "metadata" : true,
+      "pi_omit" : true
+    }
+  ],
+  "header_stacks" : [],
+  "header_union_types" : [],
+  "header_unions" : [],
+  "header_union_stacks" : [],
+  "field_lists" : [],
+  "errors" : [
+    ["NoError", 1],
+    ["PacketTooShort", 2],
+    ["NoMatch", 3],
+    ["StackOutOfBounds", 4],
+    ["HeaderTooShort", 5],
+    ["ParserTimeout", 6]
+  ],
+  "enums" : [],
+  "parsers" : [
+    {
+      "name" : "parser",
+      "id" : 0,
+      "init_state" : "start",
+      "parse_states" : [
+        {
+          "name" : "start",
+          "id" : 0,
+          "parser_ops" : [],
+          "transitions" : [
+            {
+              "value" : "0x00ff",
+              "mask" : null,
+              "next_state" : "parse_packet_out"
+            },
+            {
+              "value" : "default",
+              "mask" : null,
+              "next_state" : "parse_ethernet"
+            }
+          ],
+          "transition_key" : [
+            {
+              "type" : "field",
+              "value" : ["standard_metadata", "ingress_port"]
+            }
+          ]
+        },
+        {
+          "name" : "parse_packet_out",
+          "id" : 1,
+          "parser_ops" : [
+            {
+              "parameters" : [
+                {
+                  "type" : "regular",
+                  "value" : "packet_out"
+                }
+              ],
+              "op" : "extract"
+            }
+          ],
+          "transitions" : [
+            {
+              "value" : "default",
+              "mask" : null,
+              "next_state" : "parse_ethernet"
+            }
+          ],
+          "transition_key" : []
+        },
+        {
+          "name" : "parse_ethernet",
+          "id" : 2,
+          "parser_ops" : [
+            {
+              "parameters" : [
+                {
+                  "type" : "regular",
+                  "value" : "ethernet"
+                }
+              ],
+              "op" : "extract"
+            },
+            {
+              "parameters" : [
+                {
+                  "type" : "field",
+                  "value" : ["scalars", "fabric_metadata_t.original_ether_type"]
+                },
+                {
+                  "type" : "field",
+                  "value" : ["ethernet", "ether_type"]
+                }
+              ],
+              "op" : "set"
+            }
+          ],
+          "transitions" : [
+            {
+              "value" : "0x8100",
+              "mask" : null,
+              "next_state" : "parse_vlan_tag"
+            },
+            {
+              "value" : "0x8847",
+              "mask" : null,
+              "next_state" : "parse_mpls"
+            },
+            {
+              "value" : "0x0806",
+              "mask" : null,
+              "next_state" : "parse_arp"
+            },
+            {
+              "value" : "0x0800",
+              "mask" : null,
+              "next_state" : "parse_ipv4"
+            },
+            {
+              "value" : "default",
+              "mask" : null,
+              "next_state" : null
+            }
+          ],
+          "transition_key" : [
+            {
+              "type" : "field",
+              "value" : ["ethernet", "ether_type"]
+            }
+          ]
+        },
+        {
+          "name" : "parse_vlan_tag",
+          "id" : 3,
+          "parser_ops" : [
+            {
+              "parameters" : [
+                {
+                  "type" : "regular",
+                  "value" : "vlan_tag"
+                }
+              ],
+              "op" : "extract"
+            }
+          ],
+          "transitions" : [
+            {
+              "value" : "0x0806",
+              "mask" : null,
+              "next_state" : "parse_arp"
+            },
+            {
+              "value" : "0x0800",
+              "mask" : null,
+              "next_state" : "parse_ipv4"
+            },
+            {
+              "value" : "0x8847",
+              "mask" : null,
+              "next_state" : "parse_mpls"
+            },
+            {
+              "value" : "default",
+              "mask" : null,
+              "next_state" : null
+            }
+          ],
+          "transition_key" : [
+            {
+              "type" : "field",
+              "value" : ["vlan_tag", "ether_type"]
+            }
+          ]
+        },
+        {
+          "name" : "parse_mpls",
+          "id" : 4,
+          "parser_ops" : [
+            {
+              "parameters" : [
+                {
+                  "type" : "regular",
+                  "value" : "mpls"
+                }
+              ],
+              "op" : "extract"
+            },
+            {
+              "parameters" : [
+                {
+                  "type" : "field",
+                  "value" : ["scalars", "tmp"]
+                },
+                {
+                  "type" : "lookahead",
+                  "value" : [0, 4]
+                }
+              ],
+              "op" : "set"
+            }
+          ],
+          "transitions" : [
+            {
+              "value" : "0x04",
+              "mask" : null,
+              "next_state" : "parse_ipv4"
+            },
+            {
+              "value" : "default",
+              "mask" : null,
+              "next_state" : "parse_ethernet"
+            }
+          ],
+          "transition_key" : [
+            {
+              "type" : "field",
+              "value" : ["scalars", "tmp"]
+            }
+          ]
+        },
+        {
+          "name" : "parse_ipv4",
+          "id" : 5,
+          "parser_ops" : [
+            {
+              "parameters" : [
+                {
+                  "type" : "regular",
+                  "value" : "ipv4"
+                }
+              ],
+              "op" : "extract"
+            },
+            {
+              "parameters" : [
+                {
+                  "type" : "field",
+                  "value" : ["scalars", "fabric_metadata_t.ip_proto"]
+                },
+                {
+                  "type" : "field",
+                  "value" : ["ipv4", "protocol"]
+                }
+              ],
+              "op" : "set"
+            }
+          ],
+          "transitions" : [
+            {
+              "value" : "0x06",
+              "mask" : null,
+              "next_state" : "parse_tcp"
+            },
+            {
+              "value" : "0x11",
+              "mask" : null,
+              "next_state" : "parse_udp"
+            },
+            {
+              "value" : "0x01",
+              "mask" : null,
+              "next_state" : "parse_icmp"
+            },
+            {
+              "value" : "default",
+              "mask" : null,
+              "next_state" : null
+            }
+          ],
+          "transition_key" : [
+            {
+              "type" : "field",
+              "value" : ["ipv4", "protocol"]
+            }
+          ]
+        },
+        {
+          "name" : "parse_arp",
+          "id" : 6,
+          "parser_ops" : [
+            {
+              "parameters" : [
+                {
+                  "type" : "regular",
+                  "value" : "arp"
+                }
+              ],
+              "op" : "extract"
+            }
+          ],
+          "transitions" : [
+            {
+              "value" : "default",
+              "mask" : null,
+              "next_state" : null
+            }
+          ],
+          "transition_key" : []
+        },
+        {
+          "name" : "parse_tcp",
+          "id" : 7,
+          "parser_ops" : [
+            {
+              "parameters" : [
+                {
+                  "type" : "regular",
+                  "value" : "tcp"
+                }
+              ],
+              "op" : "extract"
+            },
+            {
+              "parameters" : [
+                {
+                  "type" : "field",
+                  "value" : ["scalars", "fabric_metadata_t.l4_src_port"]
+                },
+                {
+                  "type" : "field",
+                  "value" : ["tcp", "src_port"]
+                }
+              ],
+              "op" : "set"
+            },
+            {
+              "parameters" : [
+                {
+                  "type" : "field",
+                  "value" : ["scalars", "fabric_metadata_t.l4_dst_port"]
+                },
+                {
+                  "type" : "field",
+                  "value" : ["tcp", "dst_port"]
+                }
+              ],
+              "op" : "set"
+            }
+          ],
+          "transitions" : [
+            {
+              "value" : "default",
+              "mask" : null,
+              "next_state" : null
+            }
+          ],
+          "transition_key" : []
+        },
+        {
+          "name" : "parse_udp",
+          "id" : 8,
+          "parser_ops" : [
+            {
+              "parameters" : [
+                {
+                  "type" : "regular",
+                  "value" : "udp"
+                }
+              ],
+              "op" : "extract"
+            },
+            {
+              "parameters" : [
+                {
+                  "type" : "field",
+                  "value" : ["scalars", "fabric_metadata_t.l4_src_port"]
+                },
+                {
+                  "type" : "field",
+                  "value" : ["udp", "src_port"]
+                }
+              ],
+              "op" : "set"
+            },
+            {
+              "parameters" : [
+                {
+                  "type" : "field",
+                  "value" : ["scalars", "fabric_metadata_t.l4_dst_port"]
+                },
+                {
+                  "type" : "field",
+                  "value" : ["udp", "dst_port"]
+                }
+              ],
+              "op" : "set"
+            }
+          ],
+          "transitions" : [
+            {
+              "value" : "0x0868",
+              "mask" : null,
+              "next_state" : "parse_gtpu"
+            },
+            {
+              "value" : "default",
+              "mask" : null,
+              "next_state" : null
+            }
+          ],
+          "transition_key" : [
+            {
+              "type" : "field",
+              "value" : ["udp", "dst_port"]
+            }
+          ]
+        },
+        {
+          "name" : "parse_icmp",
+          "id" : 9,
+          "parser_ops" : [
+            {
+              "parameters" : [
+                {
+                  "type" : "regular",
+                  "value" : "icmp"
+                }
+              ],
+              "op" : "extract"
+            }
+          ],
+          "transitions" : [
+            {
+              "value" : "default",
+              "mask" : null,
+              "next_state" : null
+            }
+          ],
+          "transition_key" : []
+        },
+        {
+          "name" : "parse_gtpu",
+          "id" : 10,
+          "parser_ops" : [
+            {
+              "parameters" : [
+                {
+                  "type" : "regular",
+                  "value" : "gtpu"
+                }
+              ],
+              "op" : "extract"
+            },
+            {
+              "parameters" : [
+                {
+                  "type" : "regular",
+                  "value" : "gtpu_ipv4"
+                }
+              ],
+              "op" : "extract"
+            }
+          ],
+          "transitions" : [
+            {
+              "value" : "0x06",
+              "mask" : null,
+              "next_state" : "parse_tcp"
+            },
+            {
+              "value" : "0x11",
+              "mask" : null,
+              "next_state" : "parse_udp_inner"
+            },
+            {
+              "value" : "0x01",
+              "mask" : null,
+              "next_state" : "parse_icmp"
+            },
+            {
+              "value" : "default",
+              "mask" : null,
+              "next_state" : null
+            }
+          ],
+          "transition_key" : [
+            {
+              "type" : "field",
+              "value" : ["gtpu_ipv4", "protocol"]
+            }
+          ]
+        },
+        {
+          "name" : "parse_udp_inner",
+          "id" : 11,
+          "parser_ops" : [
+            {
+              "parameters" : [
+                {
+                  "type" : "regular",
+                  "value" : "gtpu_udp"
+                }
+              ],
+              "op" : "extract"
+            },
+            {
+              "parameters" : [
+                {
+                  "type" : "field",
+                  "value" : ["scalars", "fabric_metadata_t.l4_src_port"]
+                },
+                {
+                  "type" : "field",
+                  "value" : ["gtpu_udp", "src_port"]
+                }
+              ],
+              "op" : "set"
+            },
+            {
+              "parameters" : [
+                {
+                  "type" : "field",
+                  "value" : ["scalars", "fabric_metadata_t.l4_dst_port"]
+                },
+                {
+                  "type" : "field",
+                  "value" : ["gtpu_udp", "dst_port"]
+                }
+              ],
+              "op" : "set"
+            }
+          ],
+          "transitions" : [
+            {
+              "value" : "default",
+              "mask" : null,
+              "next_state" : null
+            }
+          ],
+          "transition_key" : []
+        }
+      ]
+    }
+  ],
+  "deparsers" : [
+    {
+      "name" : "deparser",
+      "id" : 0,
+      "source_info" : {
+        "filename" : "include/parser.p4",
+        "line" : 164,
+        "column" : 8,
+        "source_fragment" : "FabricDeparser"
+      },
+      "order" : ["packet_in", "ethernet", "vlan_tag", "mpls", "arp", "gtpu_ipv4", "gtpu_udp", "gtpu", "ipv4", "tcp", "udp", "icmp"]
+    }
+  ],
+  "meter_arrays" : [],
+  "counter_arrays" : [
+    {
+      "name" : "spgw_ingress.ue_counter",
+      "id" : 0,
+      "is_direct" : true,
+      "binding" : "spgw_ingress.ue_cdr_table"
+    },
+    {
+      "name" : "filtering.ingress_port_vlan_counter",
+      "id" : 1,
+      "is_direct" : true,
+      "binding" : "filtering.ingress_port_vlan"
+    },
+    {
+      "name" : "filtering.fwd_classifier_counter",
+      "id" : 2,
+      "is_direct" : true,
+      "binding" : "filtering.fwd_classifier"
+    },
+    {
+      "name" : "forwarding.bridging_counter",
+      "id" : 3,
+      "is_direct" : true,
+      "binding" : "forwarding.bridging"
+    },
+    {
+      "name" : "forwarding.mpls_counter",
+      "id" : 4,
+      "is_direct" : true,
+      "binding" : "forwarding.mpls"
+    },
+    {
+      "name" : "forwarding.unicast_v4_counter",
+      "id" : 5,
+      "is_direct" : true,
+      "binding" : "forwarding.unicast_v4"
+    },
+    {
+      "name" : "forwarding.multicast_v4_counter",
+      "id" : 6,
+      "is_direct" : true,
+      "binding" : "forwarding.multicast_v4"
+    },
+    {
+      "name" : "forwarding.acl_counter",
+      "id" : 7,
+      "is_direct" : true,
+      "binding" : "forwarding.acl"
+    },
+    {
+      "name" : "next.simple_counter",
+      "id" : 8,
+      "is_direct" : true,
+      "binding" : "next.simple"
+    },
+    {
+      "name" : "next.hashed_counter",
+      "id" : 9,
+      "is_direct" : true,
+      "binding" : "next.hashed"
+    },
+    {
+      "name" : "next.broadcast_counter",
+      "id" : 10,
+      "is_direct" : true,
+      "binding" : "next.broadcast"
+    },
+    {
+      "name" : "port_counters_control.egress_port_counter",
+      "id" : 11,
+      "source_info" : {
+        "filename" : "include/control/port_counter.p4",
+        "line" : 23,
+        "column" : 38,
+        "source_fragment" : "egress_port_counter"
+      },
+      "size" : 511,
+      "is_direct" : false
+    },
+    {
+      "name" : "port_counters_control.ingress_port_counter",
+      "id" : 12,
+      "source_info" : {
+        "filename" : "include/control/port_counter.p4",
+        "line" : 24,
+        "column" : 38,
+        "source_fragment" : "ingress_port_counter"
+      },
+      "size" : 511,
+      "is_direct" : false
+    }
+  ],
+  "register_arrays" : [],
+  "calculations" : [
+    {
+      "name" : "calc",
+      "id" : 0,
+      "source_info" : {
+        "filename" : "include/checksum.p4",
+        "line" : 56,
+        "column" : 8,
+        "source_fragment" : "verify_checksum(hdr.ipv4.isValid(), ..."
+      },
+      "algo" : "csum16",
+      "input" : [
+        {
+          "type" : "field",
+          "value" : ["ipv4", "version"]
+        },
+        {
+          "type" : "field",
+          "value" : ["ipv4", "ihl"]
+        },
+        {
+          "type" : "field",
+          "value" : ["ipv4", "diffserv"]
+        },
+        {
+          "type" : "field",
+          "value" : ["ipv4", "total_len"]
+        },
+        {
+          "type" : "field",
+          "value" : ["ipv4", "identification"]
+        },
+        {
+          "type" : "field",
+          "value" : ["ipv4", "flags"]
+        },
+        {
+          "type" : "field",
+          "value" : ["ipv4", "frag_offset"]
+        },
+        {
+          "type" : "field",
+          "value" : ["ipv4", "ttl"]
+        },
+        {
+          "type" : "field",
+          "value" : ["ipv4", "protocol"]
+        },
+        {
+          "type" : "field",
+          "value" : ["ipv4", "src_addr"]
+        },
+        {
+          "type" : "field",
+          "value" : ["ipv4", "dst_addr"]
+        }
+      ]
+    },
+    {
+      "name" : "calc_0",
+      "id" : 1,
+      "source_info" : {
+        "filename" : "include/checksum.p4",
+        "line" : 28,
+        "column" : 8,
+        "source_fragment" : "update_checksum(hdr.ipv4.isValid(), ..."
+      },
+      "algo" : "csum16",
+      "input" : [
+        {
+          "type" : "field",
+          "value" : ["ipv4", "version"]
+        },
+        {
+          "type" : "field",
+          "value" : ["ipv4", "ihl"]
+        },
+        {
+          "type" : "field",
+          "value" : ["ipv4", "diffserv"]
+        },
+        {
+          "type" : "field",
+          "value" : ["ipv4", "total_len"]
+        },
+        {
+          "type" : "field",
+          "value" : ["ipv4", "identification"]
+        },
+        {
+          "type" : "field",
+          "value" : ["ipv4", "flags"]
+        },
+        {
+          "type" : "field",
+          "value" : ["ipv4", "frag_offset"]
+        },
+        {
+          "type" : "field",
+          "value" : ["ipv4", "ttl"]
+        },
+        {
+          "type" : "field",
+          "value" : ["ipv4", "protocol"]
+        },
+        {
+          "type" : "field",
+          "value" : ["ipv4", "src_addr"]
+        },
+        {
+          "type" : "field",
+          "value" : ["ipv4", "dst_addr"]
+        }
+      ]
+    },
+    {
+      "name" : "calc_1",
+      "id" : 2,
+      "source_info" : {
+        "filename" : "include/spgw.p4",
+        "line" : 292,
+        "column" : 8,
+        "source_fragment" : "update_checksum(gtpu_ipv4.isValid(), ..."
+      },
+      "algo" : "csum16",
+      "input" : [
+        {
+          "type" : "field",
+          "value" : ["gtpu_ipv4", "version"]
+        },
+        {
+          "type" : "field",
+          "value" : ["gtpu_ipv4", "ihl"]
+        },
+        {
+          "type" : "field",
+          "value" : ["gtpu_ipv4", "diffserv"]
+        },
+        {
+          "type" : "field",
+          "value" : ["gtpu_ipv4", "total_len"]
+        },
+        {
+          "type" : "field",
+          "value" : ["gtpu_ipv4", "identification"]
+        },
+        {
+          "type" : "field",
+          "value" : ["gtpu_ipv4", "flags"]
+        },
+        {
+          "type" : "field",
+          "value" : ["gtpu_ipv4", "frag_offset"]
+        },
+        {
+          "type" : "field",
+          "value" : ["gtpu_ipv4", "ttl"]
+        },
+        {
+          "type" : "field",
+          "value" : ["gtpu_ipv4", "protocol"]
+        },
+        {
+          "type" : "field",
+          "value" : ["gtpu_ipv4", "src_addr"]
+        },
+        {
+          "type" : "field",
+          "value" : ["gtpu_ipv4", "dst_addr"]
+        }
+      ]
+    },
+    {
+      "name" : "calc_2",
+      "id" : 3,
+      "source_info" : {
+        "filename" : "include/spgw.p4",
+        "line" : 311,
+        "column" : 8,
+        "source_fragment" : "update_checksum_with_payload(gtpu_udp.isValid(), ..."
+      },
+      "algo" : "csum16",
+      "input" : [
+        {
+          "type" : "field",
+          "value" : ["gtpu_ipv4", "src_addr"]
+        },
+        {
+          "type" : "field",
+          "value" : ["gtpu_ipv4", "dst_addr"]
+        },
+        {
+          "type" : "hexstr",
+          "value" : "0x00",
+          "bitwidth" : 8
+        },
+        {
+          "type" : "field",
+          "value" : ["gtpu_ipv4", "protocol"]
+        },
+        {
+          "type" : "field",
+          "value" : ["gtpu_udp", "len"]
+        },
+        {
+          "type" : "field",
+          "value" : ["gtpu_udp", "src_port"]
+        },
+        {
+          "type" : "field",
+          "value" : ["gtpu_udp", "dst_port"]
+        },
+        {
+          "type" : "field",
+          "value" : ["gtpu_udp", "len"]
+        },
+        {
+          "type" : "header",
+          "value" : "gtpu"
+        },
+        {
+          "type" : "header",
+          "value" : "ipv4"
+        },
+        {
+          "type" : "header",
+          "value" : "udp"
+        },
+        {
+          "type" : "payload",
+          "value" : null
+        }
+      ]
+    }
+  ],
+  "learn_lists" : [],
+  "actions" : [
+    {
+      "name" : "NoAction",
+      "id" : 0,
+      "runtime_data" : [],
+      "primitives" : []
+    },
+    {
+      "name" : "NoAction",
+      "id" : 1,
+      "runtime_data" : [],
+      "primitives" : []
+    },
+    {
+      "name" : "NoAction",
+      "id" : 2,
+      "runtime_data" : [],
+      "primitives" : []
+    },
+    {
+      "name" : "NoAction",
+      "id" : 3,
+      "runtime_data" : [],
+      "primitives" : []
+    },
+    {
+      "name" : "NoAction",
+      "id" : 4,
+      "runtime_data" : [],
+      "primitives" : []
+    },
+    {
+      "name" : "NoAction",
+      "id" : 5,
+      "runtime_data" : [],
+      "primitives" : []
+    },
+    {
+      "name" : "NoAction",
+      "id" : 6,
+      "runtime_data" : [],
+      "primitives" : []
+    },
+    {
+      "name" : "NoAction",
+      "id" : 7,
+      "runtime_data" : [],
+      "primitives" : []
+    },
+    {
+      "name" : "NoAction",
+      "id" : 8,
+      "runtime_data" : [],
+      "primitives" : []
+    },
+    {
+      "name" : "NoAction",
+      "id" : 9,
+      "runtime_data" : [],
+      "primitives" : []
+    },
+    {
+      "name" : "NoAction",
+      "id" : 10,
+      "runtime_data" : [],
+      "primitives" : []
+    },
+    {
+      "name" : "nop",
+      "id" : 11,
+      "runtime_data" : [],
+      "primitives" : []
+    },
+    {
+      "name" : "nop",
+      "id" : 12,
+      "runtime_data" : [],
+      "primitives" : []
+    },
+    {
+      "name" : "spgw_ingress.drop_now",
+      "id" : 13,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "drop",
+          "parameters" : [],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 33,
+            "column" : 8,
+            "source_fragment" : "mark_to_drop()"
+          }
+        },
+        {
+          "op" : "exit",
+          "parameters" : [],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 34,
+            "column" : 8,
+            "source_fragment" : "exit"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "spgw_ingress.gtpu_decap",
+      "id" : 14,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "remove_header",
+          "parameters" : [
+            {
+              "type" : "header",
+              "value" : "gtpu_ipv4"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 38,
+            "column" : 8,
+            "source_fragment" : "gtpu_ipv4.setInvalid()"
+          }
+        },
+        {
+          "op" : "remove_header",
+          "parameters" : [
+            {
+              "type" : "header",
+              "value" : "gtpu_udp"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 39,
+            "column" : 8,
+            "source_fragment" : "gtpu_udp.setInvalid()"
+          }
+        },
+        {
+          "op" : "remove_header",
+          "parameters" : [
+            {
+              "type" : "header",
+              "value" : "gtpu"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 40,
+            "column" : 8,
+            "source_fragment" : "gtpu.setInvalid()"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "spgw_ingress.set_dl_sess_info",
+      "id" : 15,
+      "runtime_data" : [
+        {
+          "name" : "teid",
+          "bitwidth" : 32
+        },
+        {
+          "name" : "s1u_enb_addr",
+          "bitwidth" : 32
+        },
+        {
+          "name" : "s1u_sgw_addr",
+          "bitwidth" : 32
+        }
+      ],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["spgw", "teid"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 0
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 46,
+            "column" : 8,
+            "source_fragment" : "spgw_meta.teid = teid"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["spgw", "s1u_enb_addr"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 1
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 47,
+            "column" : 8,
+            "source_fragment" : "spgw_meta.s1u_enb_addr = s1u_enb_addr"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["spgw", "s1u_sgw_addr"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 2
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 48,
+            "column" : 8,
+            "source_fragment" : "spgw_meta.s1u_sgw_addr = s1u_sgw_addr"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "spgw_ingress.update_ue_cdr",
+      "id" : 16,
+      "runtime_data" : [],
+      "primitives" : []
+    },
+    {
+      "name" : "filtering.drop",
+      "id" : 17,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "drop",
+          "parameters" : [],
+          "source_info" : {
+            "filename" : "include/control/filtering.p4",
+            "line" : 31,
+            "column" : 8,
+            "source_fragment" : "mark_to_drop()"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "filtering.set_vlan",
+      "id" : 18,
+      "runtime_data" : [
+        {
+          "name" : "new_vlan_id",
+          "bitwidth" : 12
+        }
+      ],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["vlan_tag", "vlan_id"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 0
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/filtering.p4",
+            "line" : 35,
+            "column" : 8,
+            "source_fragment" : "hdr.vlan_tag.vlan_id = new_vlan_id"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "filtering.push_internal_vlan",
+      "id" : 19,
+      "runtime_data" : [
+        {
+          "name" : "new_vlan_id",
+          "bitwidth" : 12
+        }
+      ],
+      "primitives" : [
+        {
+          "op" : "add_header",
+          "parameters" : [
+            {
+              "type" : "header",
+              "value" : "vlan_tag"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/filtering.p4",
+            "line" : 41,
+            "column" : 8,
+            "source_fragment" : "hdr.vlan_tag.setValid()"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["vlan_tag", "cfi"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x00"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/filtering.p4",
+            "line" : 42,
+            "column" : 8,
+            "source_fragment" : "hdr.vlan_tag.cfi = 0"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["vlan_tag", "pri"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x00"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/filtering.p4",
+            "line" : 43,
+            "column" : 8,
+            "source_fragment" : "hdr.vlan_tag.pri = 0"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["vlan_tag", "ether_type"]
+            },
+            {
+              "type" : "field",
+              "value" : ["ethernet", "ether_type"]
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/filtering.p4",
+            "line" : 44,
+            "column" : 8,
+            "source_fragment" : "hdr.vlan_tag.ether_type = hdr.ethernet.ether_type"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["ethernet", "ether_type"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x8100"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/../define.p4",
+            "line" : 32,
+            "column" : 31,
+            "source_fragment" : "0x8100; ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["vlan_tag", "vlan_id"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 0
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/filtering.p4",
+            "line" : 35,
+            "column" : 8,
+            "source_fragment" : "hdr.vlan_tag.vlan_id = new_vlan_id; ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["scalars", "fabric_metadata_t.pop_vlan_at_egress"]
+            },
+            {
+              "type" : "expression",
+              "value" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "b2d",
+                  "left" : null,
+                  "right" : {
+                    "type" : "bool",
+                    "value" : true
+                  }
+                }
+              }
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/filtering.p4",
+            "line" : 49,
+            "column" : 8,
+            "source_fragment" : "fabric_metadata.pop_vlan_at_egress = true"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "filtering.set_forwarding_type",
+      "id" : 20,
+      "runtime_data" : [
+        {
+          "name" : "fwd_type",
+          "bitwidth" : 3
+        }
+      ],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["scalars", "fabric_metadata_t.fwd_type"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 0
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/filtering.p4",
+            "line" : 53,
+            "column" : 8,
+            "source_fragment" : "fabric_metadata.fwd_type = fwd_type"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "forwarding.drop",
+      "id" : 21,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "drop",
+          "parameters" : [],
+          "source_info" : {
+            "filename" : "include/control/forwarding.p4",
+            "line" : 37,
+            "column" : 8,
+            "source_fragment" : "mark_to_drop()"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "forwarding.set_next_id",
+      "id" : 22,
+      "runtime_data" : [
+        {
+          "name" : "next_id",
+          "bitwidth" : 32
+        }
+      ],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["scalars", "fabric_metadata_t.next_id"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 0
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/forwarding.p4",
+            "line" : 41,
+            "column" : 8,
+            "source_fragment" : "fabric_metadata.next_id = next_id"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "forwarding.set_next_id",
+      "id" : 23,
+      "runtime_data" : [
+        {
+          "name" : "next_id",
+          "bitwidth" : 32
+        }
+      ],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["scalars", "fabric_metadata_t.next_id"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 0
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/forwarding.p4",
+            "line" : 41,
+            "column" : 8,
+            "source_fragment" : "fabric_metadata.next_id = next_id"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "forwarding.set_next_id",
+      "id" : 24,
+      "runtime_data" : [
+        {
+          "name" : "next_id",
+          "bitwidth" : 32
+        }
+      ],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["scalars", "fabric_metadata_t.next_id"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 0
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/forwarding.p4",
+            "line" : 41,
+            "column" : 8,
+            "source_fragment" : "fabric_metadata.next_id = next_id"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "forwarding.set_next_id",
+      "id" : 25,
+      "runtime_data" : [
+        {
+          "name" : "next_id",
+          "bitwidth" : 32
+        }
+      ],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["scalars", "fabric_metadata_t.next_id"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 0
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/forwarding.p4",
+            "line" : 41,
+            "column" : 8,
+            "source_fragment" : "fabric_metadata.next_id = next_id"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "forwarding.pop_mpls_and_next",
+      "id" : 26,
+      "runtime_data" : [
+        {
+          "name" : "next_id",
+          "bitwidth" : 32
+        }
+      ],
+      "primitives" : [
+        {
+          "op" : "remove_header",
+          "parameters" : [
+            {
+              "type" : "header",
+              "value" : "mpls"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/forwarding.p4",
+            "line" : 45,
+            "column" : 8,
+            "source_fragment" : "hdr.mpls.setInvalid()"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["scalars", "fabric_metadata_t.next_id"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 0
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/forwarding.p4",
+            "line" : 46,
+            "column" : 8,
+            "source_fragment" : "fabric_metadata.next_id = next_id"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "forwarding.duplicate_to_controller",
+      "id" : 27,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["standard_metadata", "egress_spec"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x00ff"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/../define.p4",
+            "line" : 50,
+            "column" : 28,
+            "source_fragment" : "255; ..."
+          }
+        }
+      ]
+    },
+    {
+      "name" : "next.output",
+      "id" : 28,
+      "runtime_data" : [
+        {
+          "name" : "port_num",
+          "bitwidth" : 9
+        }
+      ],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["standard_metadata", "egress_spec"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 0
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 33,
+            "column" : 8,
+            "source_fragment" : "standard_metadata.egress_spec = port_num"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "next.set_vlan_output",
+      "id" : 29,
+      "runtime_data" : [
+        {
+          "name" : "new_vlan_id",
+          "bitwidth" : 12
+        },
+        {
+          "name" : "port_num",
+          "bitwidth" : 9
+        }
+      ],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["vlan_tag", "vlan_id"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 0
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 37,
+            "column" : 8,
+            "source_fragment" : "hdr.vlan_tag.vlan_id = new_vlan_id"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["scalars", "fabric_metadata_t.pop_vlan_at_egress"]
+            },
+            {
+              "type" : "expression",
+              "value" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "b2d",
+                  "left" : null,
+                  "right" : {
+                    "type" : "bool",
+                    "value" : false
+                  }
+                }
+              }
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 40,
+            "column" : 8,
+            "source_fragment" : "fabric_metadata.pop_vlan_at_egress = false"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["standard_metadata", "egress_spec"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 1
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 33,
+            "column" : 8,
+            "source_fragment" : "standard_metadata.egress_spec = port_num; ..."
+          }
+        }
+      ]
+    },
+    {
+      "name" : "next.l3_routing",
+      "id" : 30,
+      "runtime_data" : [
+        {
+          "name" : "port_num",
+          "bitwidth" : 9
+        },
+        {
+          "name" : "smac",
+          "bitwidth" : 48
+        },
+        {
+          "name" : "dmac",
+          "bitwidth" : 48
+        }
+      ],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["ethernet", "src_addr"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 1
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 45,
+            "column" : 8,
+            "source_fragment" : "hdr.ethernet.src_addr = smac; ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["ethernet", "dst_addr"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 2
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 49,
+            "column" : 8,
+            "source_fragment" : "hdr.ethernet.dst_addr = dmac; ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["standard_metadata", "egress_spec"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 0
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 33,
+            "column" : 8,
+            "source_fragment" : "standard_metadata.egress_spec = port_num; ..."
+          }
+        }
+      ]
+    },
+    {
+      "name" : "next.l3_routing",
+      "id" : 31,
+      "runtime_data" : [
+        {
+          "name" : "port_num",
+          "bitwidth" : 9
+        },
+        {
+          "name" : "smac",
+          "bitwidth" : 48
+        },
+        {
+          "name" : "dmac",
+          "bitwidth" : 48
+        }
+      ],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["ethernet", "src_addr"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 1
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 45,
+            "column" : 8,
+            "source_fragment" : "hdr.ethernet.src_addr = smac; ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["ethernet", "dst_addr"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 2
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 49,
+            "column" : 8,
+            "source_fragment" : "hdr.ethernet.dst_addr = dmac; ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["standard_metadata", "egress_spec"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 0
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 33,
+            "column" : 8,
+            "source_fragment" : "standard_metadata.egress_spec = port_num; ..."
+          }
+        }
+      ]
+    },
+    {
+      "name" : "next.set_mcast_group",
+      "id" : 32,
+      "runtime_data" : [
+        {
+          "name" : "gid",
+          "bitwidth" : 16
+        },
+        {
+          "name" : "smac",
+          "bitwidth" : 48
+        }
+      ],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["standard_metadata", "mcast_grp"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 0
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 59,
+            "column" : 8,
+            "source_fragment" : "standard_metadata.mcast_grp = gid"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["ethernet", "src_addr"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 1
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 45,
+            "column" : 8,
+            "source_fragment" : "hdr.ethernet.src_addr = smac; ..."
+          }
+        }
+      ]
+    },
+    {
+      "name" : "next.mpls_routing_v4",
+      "id" : 33,
+      "runtime_data" : [
+        {
+          "name" : "port_num",
+          "bitwidth" : 9
+        },
+        {
+          "name" : "smac",
+          "bitwidth" : 48
+        },
+        {
+          "name" : "dmac",
+          "bitwidth" : 48
+        },
+        {
+          "name" : "label",
+          "bitwidth" : 20
+        }
+      ],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["ethernet", "src_addr"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 1
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 45,
+            "column" : 8,
+            "source_fragment" : "hdr.ethernet.src_addr = smac; ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["ethernet", "dst_addr"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 2
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 49,
+            "column" : 8,
+            "source_fragment" : "hdr.ethernet.dst_addr = dmac; ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["standard_metadata", "egress_spec"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 0
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 33,
+            "column" : 8,
+            "source_fragment" : "standard_metadata.egress_spec = port_num; ..."
+          }
+        },
+        {
+          "op" : "add_header",
+          "parameters" : [
+            {
+              "type" : "header",
+              "value" : "mpls"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 65,
+            "column" : 8,
+            "source_fragment" : "hdr.mpls.setValid()"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["vlan_tag", "ether_type"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x8847"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/../define.p4",
+            "line" : 33,
+            "column" : 31,
+            "source_fragment" : "0x8847; ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["mpls", "label"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 3
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 67,
+            "column" : 8,
+            "source_fragment" : "hdr.mpls.label = label; ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["mpls", "tc"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x00"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 68,
+            "column" : 8,
+            "source_fragment" : "hdr.mpls.tc = tc; ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["mpls", "bos"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x01"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 69,
+            "column" : 8,
+            "source_fragment" : "hdr.mpls.bos = 1w1"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["mpls", "ttl"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x40"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/../define.p4",
+            "line" : 60,
+            "column" : 32,
+            "source_fragment" : "64; ..."
+          }
+        }
+      ]
+    },
+    {
+      "name" : "next.mpls_routing_v4",
+      "id" : 34,
+      "runtime_data" : [
+        {
+          "name" : "port_num",
+          "bitwidth" : 9
+        },
+        {
+          "name" : "smac",
+          "bitwidth" : 48
+        },
+        {
+          "name" : "dmac",
+          "bitwidth" : 48
+        },
+        {
+          "name" : "label",
+          "bitwidth" : 20
+        }
+      ],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["ethernet", "src_addr"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 1
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 45,
+            "column" : 8,
+            "source_fragment" : "hdr.ethernet.src_addr = smac; ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["ethernet", "dst_addr"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 2
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 49,
+            "column" : 8,
+            "source_fragment" : "hdr.ethernet.dst_addr = dmac; ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["standard_metadata", "egress_spec"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 0
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 33,
+            "column" : 8,
+            "source_fragment" : "standard_metadata.egress_spec = port_num; ..."
+          }
+        },
+        {
+          "op" : "add_header",
+          "parameters" : [
+            {
+              "type" : "header",
+              "value" : "mpls"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 65,
+            "column" : 8,
+            "source_fragment" : "hdr.mpls.setValid()"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["vlan_tag", "ether_type"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x8847"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/../define.p4",
+            "line" : 33,
+            "column" : 31,
+            "source_fragment" : "0x8847; ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["mpls", "label"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 3
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 67,
+            "column" : 8,
+            "source_fragment" : "hdr.mpls.label = label; ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["mpls", "tc"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x00"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 68,
+            "column" : 8,
+            "source_fragment" : "hdr.mpls.tc = tc; ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["mpls", "bos"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x01"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 69,
+            "column" : 8,
+            "source_fragment" : "hdr.mpls.bos = 1w1"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["mpls", "ttl"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x40"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/../define.p4",
+            "line" : 60,
+            "column" : 32,
+            "source_fragment" : "64; ..."
+          }
+        }
+      ]
+    },
+    {
+      "name" : "next.mpls_routing_v6",
+      "id" : 35,
+      "runtime_data" : [
+        {
+          "name" : "port_num",
+          "bitwidth" : 9
+        },
+        {
+          "name" : "smac",
+          "bitwidth" : 48
+        },
+        {
+          "name" : "dmac",
+          "bitwidth" : 48
+        },
+        {
+          "name" : "label",
+          "bitwidth" : 20
+        }
+      ],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["ethernet", "src_addr"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 1
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 45,
+            "column" : 8,
+            "source_fragment" : "hdr.ethernet.src_addr = smac; ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["ethernet", "dst_addr"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 2
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 49,
+            "column" : 8,
+            "source_fragment" : "hdr.ethernet.dst_addr = dmac; ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["standard_metadata", "egress_spec"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 0
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 33,
+            "column" : 8,
+            "source_fragment" : "standard_metadata.egress_spec = port_num; ..."
+          }
+        },
+        {
+          "op" : "add_header",
+          "parameters" : [
+            {
+              "type" : "header",
+              "value" : "mpls"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 65,
+            "column" : 8,
+            "source_fragment" : "hdr.mpls.setValid()"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["vlan_tag", "ether_type"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x8847"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/../define.p4",
+            "line" : 33,
+            "column" : 31,
+            "source_fragment" : "0x8847; ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["mpls", "label"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 3
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 67,
+            "column" : 8,
+            "source_fragment" : "hdr.mpls.label = label; ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["mpls", "tc"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x00"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 68,
+            "column" : 8,
+            "source_fragment" : "hdr.mpls.tc = tc; ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["mpls", "bos"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x01"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 69,
+            "column" : 8,
+            "source_fragment" : "hdr.mpls.bos = 1w1"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["mpls", "ttl"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x40"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/../define.p4",
+            "line" : 60,
+            "column" : 32,
+            "source_fragment" : "64; ..."
+          }
+        }
+      ]
+    },
+    {
+      "name" : "act",
+      "id" : 36,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["standard_metadata", "egress_spec"]
+            },
+            {
+              "type" : "field",
+              "value" : ["packet_out", "egress_port"]
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/packetio.p4",
+            "line" : 26,
+            "column" : 12,
+            "source_fragment" : "standard_metadata.egress_spec = hdr.packet_out.egress_port"
+          }
+        },
+        {
+          "op" : "remove_header",
+          "parameters" : [
+            {
+              "type" : "header",
+              "value" : "packet_out"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/packetio.p4",
+            "line" : 27,
+            "column" : 12,
+            "source_fragment" : "hdr.packet_out.setInvalid()"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "act_0",
+      "id" : 37,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["scalars", "spgw_ingress_tmp_2"]
+            },
+            {
+              "type" : "expression",
+              "value" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "b2d",
+                  "left" : null,
+                  "right" : {
+                    "type" : "bool",
+                    "value" : true
+                  }
+                }
+              }
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name" : "act_1",
+      "id" : 38,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["scalars", "spgw_ingress_tmp_2"]
+            },
+            {
+              "type" : "expression",
+              "value" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "b2d",
+                  "left" : null,
+                  "right" : {
+                    "type" : "bool",
+                    "value" : false
+                  }
+                }
+              }
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name" : "act_2",
+      "id" : 39,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["spgw", "s1u_enb_addr"]
+            },
+            {
+              "type" : "field",
+              "value" : ["ipv4", "src_addr"]
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 152,
+            "column" : 12,
+            "source_fragment" : "spgw_meta.s1u_enb_addr = ipv4.src_addr"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["spgw", "s1u_sgw_addr"]
+            },
+            {
+              "type" : "field",
+              "value" : ["ipv4", "dst_addr"]
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 153,
+            "column" : 12,
+            "source_fragment" : "spgw_meta.s1u_sgw_addr = ipv4.dst_addr"
+          }
+        },
+        {
+          "op" : "assign_header",
+          "parameters" : [
+            {
+              "type" : "header",
+              "value" : "ipv4"
+            },
+            {
+              "type" : "header",
+              "value" : "gtpu_ipv4"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 154,
+            "column" : 17,
+            "source_fragment" : "= gtpu_ipv4; ..."
+          }
+        },
+        {
+          "op" : "assign_header",
+          "parameters" : [
+            {
+              "type" : "header",
+              "value" : "udp"
+            },
+            {
+              "type" : "header",
+              "value" : "gtpu_udp"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 155,
+            "column" : 16,
+            "source_fragment" : "= gtpu_udp; ..."
+          }
+        }
+      ]
+    },
+    {
+      "name" : "act_3",
+      "id" : 40,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["spgw", "do_spgw"]
+            },
+            {
+              "type" : "expression",
+              "value" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "b2d",
+                  "left" : null,
+                  "right" : {
+                    "type" : "bool",
+                    "value" : true
+                  }
+                }
+              }
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 159,
+            "column" : 16,
+            "source_fragment" : "spgw_meta.do_spgw = true"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["spgw", "direction"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x00"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/../define.p4",
+            "line" : 81,
+            "column" : 31,
+            "source_fragment" : "1w0; ..."
+          }
+        }
+      ]
+    },
+    {
+      "name" : "act_4",
+      "id" : 41,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["scalars", "spgw_ingress_tmp_3"]
+            },
+            {
+              "type" : "expression",
+              "value" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "b2d",
+                  "left" : null,
+                  "right" : {
+                    "type" : "bool",
+                    "value" : true
+                  }
+                }
+              }
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name" : "act_5",
+      "id" : 42,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["scalars", "spgw_ingress_tmp_3"]
+            },
+            {
+              "type" : "expression",
+              "value" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "b2d",
+                  "left" : null,
+                  "right" : {
+                    "type" : "bool",
+                    "value" : false
+                  }
+                }
+              }
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name" : "act_6",
+      "id" : 43,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["spgw", "do_spgw"]
+            },
+            {
+              "type" : "expression",
+              "value" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "b2d",
+                  "left" : null,
+                  "right" : {
+                    "type" : "bool",
+                    "value" : true
+                  }
+                }
+              }
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 163,
+            "column" : 12,
+            "source_fragment" : "spgw_meta.do_spgw = true"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["spgw", "direction"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x01"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/../define.p4",
+            "line" : 82,
+            "column" : 33,
+            "source_fragment" : "1w1; ..."
+          }
+        }
+      ]
+    },
+    {
+      "name" : "act_7",
+      "id" : 44,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["scalars", "spgw_ingress_hasReturned_0"]
+            },
+            {
+              "type" : "expression",
+              "value" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "b2d",
+                  "left" : null,
+                  "right" : {
+                    "type" : "bool",
+                    "value" : false
+                  }
+                }
+              }
+            }
+          ]
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["spgw", "do_spgw"]
+            },
+            {
+              "type" : "expression",
+              "value" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "b2d",
+                  "left" : null,
+                  "right" : {
+                    "type" : "bool",
+                    "value" : false
+                  }
+                }
+              }
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 146,
+            "column" : 8,
+            "source_fragment" : "spgw_meta.do_spgw = false"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "act_8",
+      "id" : 45,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["scalars", "spgw_ingress_hasReturned_0"]
+            },
+            {
+              "type" : "expression",
+              "value" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "b2d",
+                  "left" : null,
+                  "right" : {
+                    "type" : "bool",
+                    "value" : true
+                  }
+                }
+              }
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 169,
+            "column" : 12,
+            "source_fragment" : "return"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "act_9",
+      "id" : 46,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["scalars", "spgw_ingress_tmp_4"]
+            },
+            {
+              "type" : "expression",
+              "value" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "b2d",
+                  "left" : null,
+                  "right" : {
+                    "type" : "bool",
+                    "value" : true
+                  }
+                }
+              }
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name" : "act_10",
+      "id" : 47,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["scalars", "spgw_ingress_tmp_4"]
+            },
+            {
+              "type" : "expression",
+              "value" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "b2d",
+                  "left" : null,
+                  "right" : {
+                    "type" : "bool",
+                    "value" : false
+                  }
+                }
+              }
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name" : "act_11",
+      "id" : 48,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["vlan_tag", "ether_type"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x0800"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/../define.p4",
+            "line" : 35,
+            "column" : 31,
+            "source_fragment" : "0x0800; ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["scalars", "fabric_metadata_t.original_ether_type"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x0800"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/../define.p4",
+            "line" : 35,
+            "column" : 31,
+            "source_fragment" : "0x0800; ..."
+          }
+        }
+      ]
+    },
+    {
+      "name" : "act_12",
+      "id" : 49,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["scalars", "next_tmp_0"]
+            },
+            {
+              "type" : "expression",
+              "value" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "b2d",
+                  "left" : null,
+                  "right" : {
+                    "type" : "bool",
+                    "value" : true
+                  }
+                }
+              }
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name" : "act_13",
+      "id" : 50,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["scalars", "next_tmp_0"]
+            },
+            {
+              "type" : "expression",
+              "value" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "b2d",
+                  "left" : null,
+                  "right" : {
+                    "type" : "bool",
+                    "value" : false
+                  }
+                }
+              }
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name" : "act_14",
+      "id" : 51,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["ipv4", "ttl"]
+            },
+            {
+              "type" : "expression",
+              "value" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "&",
+                  "left" : {
+                    "type" : "expression",
+                    "value" : {
+                      "op" : "+",
+                      "left" : {
+                        "type" : "field",
+                        "value" : ["ipv4", "ttl"]
+                      },
+                      "right" : {
+                        "type" : "hexstr",
+                        "value" : "0xff"
+                      }
+                    }
+                  },
+                  "right" : {
+                    "type" : "hexstr",
+                    "value" : "0xff"
+                  }
+                }
+              }
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 140,
+            "column" : 20,
+            "source_fragment" : "hdr.ipv4.ttl = hdr.ipv4.ttl - 1"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "act_15",
+      "id" : 52,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["scalars", "tmp_0"]
+            },
+            {
+              "type" : "expression",
+              "value" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "&",
+                  "left" : {
+                    "type" : "field",
+                    "value" : ["standard_metadata", "egress_spec"]
+                  },
+                  "right" : {
+                    "type" : "hexstr",
+                    "value" : "0xffffffff"
+                  }
+                }
+              }
+            }
+          ]
+        },
+        {
+          "op" : "count",
+          "parameters" : [
+            {
+              "type" : "counter_array",
+              "value" : "port_counters_control.egress_port_counter"
+            },
+            {
+              "type" : "field",
+              "value" : ["scalars", "tmp_0"]
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/port_counter.p4",
+            "line" : 28,
+            "column" : 12,
+            "source_fragment" : "egress_port_counter.count((bit<32>)standard_metadata.egress_spec)"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "act_16",
+      "id" : 53,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["scalars", "tmp_1"]
+            },
+            {
+              "type" : "expression",
+              "value" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "&",
+                  "left" : {
+                    "type" : "field",
+                    "value" : ["standard_metadata", "ingress_port"]
+                  },
+                  "right" : {
+                    "type" : "hexstr",
+                    "value" : "0xffffffff"
+                  }
+                }
+              }
+            }
+          ]
+        },
+        {
+          "op" : "count",
+          "parameters" : [
+            {
+              "type" : "counter_array",
+              "value" : "port_counters_control.ingress_port_counter"
+            },
+            {
+              "type" : "field",
+              "value" : ["scalars", "tmp_1"]
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/port_counter.p4",
+            "line" : 31,
+            "column" : 12,
+            "source_fragment" : "ingress_port_counter.count((bit<32>)standard_metadata.ingress_port)"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "act_17",
+      "id" : 54,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["ethernet", "ether_type"]
+            },
+            {
+              "type" : "field",
+              "value" : ["vlan_tag", "ether_type"]
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 162,
+            "column" : 12,
+            "source_fragment" : "hdr.ethernet.ether_type = hdr.vlan_tag.ether_type"
+          }
+        },
+        {
+          "op" : "remove_header",
+          "parameters" : [
+            {
+              "type" : "header",
+              "value" : "vlan_tag"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 163,
+            "column" : 12,
+            "source_fragment" : "hdr.vlan_tag.setInvalid()"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "spgw_egress.gtpu_encap",
+      "id" : 55,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "add_header",
+          "parameters" : [
+            {
+              "type" : "header",
+              "value" : "gtpu_ipv4"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 210,
+            "column" : 8,
+            "source_fragment" : "gtpu_ipv4.setValid()"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["gtpu_ipv4", "version"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x04"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/../define.p4",
+            "line" : 39,
+            "column" : 28,
+            "source_fragment" : "4; ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["gtpu_ipv4", "ihl"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x05"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/../define.p4",
+            "line" : 47,
+            "column" : 28,
+            "source_fragment" : "5; ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["gtpu_ipv4", "diffserv"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x00"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 213,
+            "column" : 8,
+            "source_fragment" : "gtpu_ipv4.diffserv = 0"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["gtpu_ipv4", "total_len"]
+            },
+            {
+              "type" : "expression",
+              "value" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "&",
+                  "left" : {
+                    "type" : "expression",
+                    "value" : {
+                      "op" : "+",
+                      "left" : {
+                        "type" : "expression",
+                        "value" : {
+                          "op" : "&",
+                          "left" : {
+                            "type" : "expression",
+                            "value" : {
+                              "op" : "+",
+                              "left" : {
+                                "type" : "expression",
+                                "value" : {
+                                  "op" : "&",
+                                  "left" : {
+                                    "type" : "expression",
+                                    "value" : {
+                                      "op" : "+",
+                                      "left" : {
+                                        "type" : "expression",
+                                        "value" : {
+                                          "op" : "&",
+                                          "left" : {
+                                            "type" : "expression",
+                                            "value" : {
+                                              "op" : "+",
+                                              "left" : {
+                                                "type" : "expression",
+                                                "value" : {
+                                                  "op" : "&",
+                                                  "left" : {
+                                                    "type" : "field",
+                                                    "value" : ["standard_metadata", "packet_length"]
+                                                  },
+                                                  "right" : {
+                                                    "type" : "hexstr",
+                                                    "value" : "0xffff"
+                                                  }
+                                                }
+                                              },
+                                              "right" : {
+                                                "type" : "hexstr",
+                                                "value" : "0xfff2"
+                                              }
+                                            }
+                                          },
+                                          "right" : {
+                                            "type" : "hexstr",
+                                            "value" : "0xffff"
+                                          }
+                                        }
+                                      },
+                                      "right" : {
+                                        "type" : "hexstr",
+                                        "value" : "0x0014"
+                                      }
+                                    }
+                                  },
+                                  "right" : {
+                                    "type" : "hexstr",
+                                    "value" : "0xffff"
+                                  }
+                                }
+                              },
+                              "right" : {
+                                "type" : "hexstr",
+                                "value" : "0x0008"
+                              }
+                            }
+                          },
+                          "right" : {
+                            "type" : "hexstr",
+                            "value" : "0xffff"
+                          }
+                        }
+                      },
+                      "right" : {
+                        "type" : "hexstr",
+                        "value" : "0x0008"
+                      }
+                    }
+                  },
+                  "right" : {
+                    "type" : "hexstr",
+                    "value" : "0xffff"
+                  }
+                }
+              }
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 214,
+            "column" : 8,
+            "source_fragment" : "gtpu_ipv4.total_len = ((bit<16>)std_meta.packet_length ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["gtpu_ipv4", "identification"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x1513"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 216,
+            "column" : 8,
+            "source_fragment" : "gtpu_ipv4.identification = 0x1513"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["gtpu_ipv4", "flags"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x00"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 217,
+            "column" : 8,
+            "source_fragment" : "gtpu_ipv4.flags = 0"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["gtpu_ipv4", "frag_offset"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x0000"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 218,
+            "column" : 8,
+            "source_fragment" : "gtpu_ipv4.frag_offset = 0"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["gtpu_ipv4", "ttl"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x40"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/../define.p4",
+            "line" : 71,
+            "column" : 32,
+            "source_fragment" : "64; ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["gtpu_ipv4", "protocol"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x11"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/../define.p4",
+            "line" : 44,
+            "column" : 25,
+            "source_fragment" : "17; ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["gtpu_ipv4", "dst_addr"]
+            },
+            {
+              "type" : "field",
+              "value" : ["spgw", "s1u_enb_addr"]
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 221,
+            "column" : 8,
+            "source_fragment" : "gtpu_ipv4.dst_addr = spgw_meta.s1u_enb_addr"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["gtpu_ipv4", "src_addr"]
+            },
+            {
+              "type" : "field",
+              "value" : ["spgw", "s1u_sgw_addr"]
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 222,
+            "column" : 8,
+            "source_fragment" : "gtpu_ipv4.src_addr = spgw_meta.s1u_sgw_addr"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["gtpu_ipv4", "hdr_checksum"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x0000"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 223,
+            "column" : 8,
+            "source_fragment" : "gtpu_ipv4.hdr_checksum = 0"
+          }
+        },
+        {
+          "op" : "add_header",
+          "parameters" : [
+            {
+              "type" : "header",
+              "value" : "gtpu_udp"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 225,
+            "column" : 8,
+            "source_fragment" : "gtpu_udp.setValid()"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["gtpu_udp", "src_port"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x0868"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 226,
+            "column" : 8,
+            "source_fragment" : "gtpu_udp.src_port = 2152"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["gtpu_udp", "dst_port"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x0868"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 227,
+            "column" : 8,
+            "source_fragment" : "gtpu_udp.dst_port = 2152"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["gtpu_udp", "len"]
+            },
+            {
+              "type" : "expression",
+              "value" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "&",
+                  "left" : {
+                    "type" : "expression",
+                    "value" : {
+                      "op" : "+",
+                      "left" : {
+                        "type" : "expression",
+                        "value" : {
+                          "op" : "&",
+                          "left" : {
+                            "type" : "expression",
+                            "value" : {
+                              "op" : "+",
+                              "left" : {
+                                "type" : "expression",
+                                "value" : {
+                                  "op" : "&",
+                                  "left" : {
+                                    "type" : "expression",
+                                    "value" : {
+                                      "op" : "+",
+                                      "left" : {
+                                        "type" : "expression",
+                                        "value" : {
+                                          "op" : "&",
+                                          "left" : {
+                                            "type" : "field",
+                                            "value" : ["standard_metadata", "packet_length"]
+                                          },
+                                          "right" : {
+                                            "type" : "hexstr",
+                                            "value" : "0xffff"
+                                          }
+                                        }
+                                      },
+                                      "right" : {
+                                        "type" : "hexstr",
+                                        "value" : "0xfff2"
+                                      }
+                                    }
+                                  },
+                                  "right" : {
+                                    "type" : "hexstr",
+                                    "value" : "0xffff"
+                                  }
+                                }
+                              },
+                              "right" : {
+                                "type" : "hexstr",
+                                "value" : "0x0008"
+                              }
+                            }
+                          },
+                          "right" : {
+                            "type" : "hexstr",
+                            "value" : "0xffff"
+                          }
+                        }
+                      },
+                      "right" : {
+                        "type" : "hexstr",
+                        "value" : "0x0008"
+                      }
+                    }
+                  },
+                  "right" : {
+                    "type" : "hexstr",
+                    "value" : "0xffff"
+                  }
+                }
+              }
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 228,
+            "column" : 8,
+            "source_fragment" : "gtpu_udp.len = ((bit<16>)std_meta.packet_length ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["gtpu_udp", "checksum"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x0000"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 230,
+            "column" : 8,
+            "source_fragment" : "gtpu_udp.checksum = 0"
+          }
+        },
+        {
+          "op" : "add_header",
+          "parameters" : [
+            {
+              "type" : "header",
+              "value" : "gtpu"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 232,
+            "column" : 8,
+            "source_fragment" : "gtpu.setValid()"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["gtpu", "version"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x01"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 233,
+            "column" : 8,
+            "source_fragment" : "gtpu.version = 0x01"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["gtpu", "pt"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x01"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 234,
+            "column" : 8,
+            "source_fragment" : "gtpu.pt = 0x01"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["gtpu", "spare"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x00"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 235,
+            "column" : 8,
+            "source_fragment" : "gtpu.spare = 0"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["gtpu", "ex_flag"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x00"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 236,
+            "column" : 8,
+            "source_fragment" : "gtpu.ex_flag = 0"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["gtpu", "seq_flag"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x00"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 237,
+            "column" : 8,
+            "source_fragment" : "gtpu.seq_flag = 0"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["gtpu", "npdu_flag"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x00"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 238,
+            "column" : 8,
+            "source_fragment" : "gtpu.npdu_flag = 0"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["gtpu", "msgtype"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0xff"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 239,
+            "column" : 8,
+            "source_fragment" : "gtpu.msgtype = 0xff"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["gtpu", "msglen"]
+            },
+            {
+              "type" : "expression",
+              "value" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "&",
+                  "left" : {
+                    "type" : "expression",
+                    "value" : {
+                      "op" : "+",
+                      "left" : {
+                        "type" : "expression",
+                        "value" : {
+                          "op" : "&",
+                          "left" : {
+                            "type" : "field",
+                            "value" : ["standard_metadata", "packet_length"]
+                          },
+                          "right" : {
+                            "type" : "hexstr",
+                            "value" : "0xffff"
+                          }
+                        }
+                      },
+                      "right" : {
+                        "type" : "hexstr",
+                        "value" : "0xfff2"
+                      }
+                    }
+                  },
+                  "right" : {
+                    "type" : "hexstr",
+                    "value" : "0xffff"
+                  }
+                }
+              }
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 240,
+            "column" : 8,
+            "source_fragment" : "gtpu.msglen = ((bit<16>)std_meta.packet_length - 14"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["gtpu", "teid"]
+            },
+            {
+              "type" : "field",
+              "value" : ["spgw", "teid"]
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 241,
+            "column" : 8,
+            "source_fragment" : "gtpu.teid = spgw_meta.teid"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "act_18",
+      "id" : 56,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "add_header",
+          "parameters" : [
+            {
+              "type" : "header",
+              "value" : "packet_in"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/packetio.p4",
+            "line" : 39,
+            "column" : 12,
+            "source_fragment" : "hdr.packet_in.setValid()"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["packet_in", "ingress_port"]
+            },
+            {
+              "type" : "field",
+              "value" : ["standard_metadata", "ingress_port"]
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/packetio.p4",
+            "line" : 40,
+            "column" : 12,
+            "source_fragment" : "hdr.packet_in.ingress_port = standard_metadata.ingress_port"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "act_19",
+      "id" : 57,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "remove_header",
+          "parameters" : [
+            {
+              "type" : "header",
+              "value" : "gtpu_ipv4"
+            }
+          ],
+          "source_info" : {
+            "filename" : "fabric.p4",
+            "line" : 69,
+            "column" : 26,
+            "source_fragment" : "hdr.gtpu_ipv4"
+          }
+        },
+        {
+          "op" : "remove_header",
+          "parameters" : [
+            {
+              "type" : "header",
+              "value" : "gtpu_udp"
+            }
+          ],
+          "source_info" : {
+            "filename" : "fabric.p4",
+            "line" : 69,
+            "column" : 41,
+            "source_fragment" : "hdr.gtpu_udp"
+          }
+        },
+        {
+          "op" : "remove_header",
+          "parameters" : [
+            {
+              "type" : "header",
+              "value" : "gtpu"
+            }
+          ],
+          "source_info" : {
+            "filename" : "fabric.p4",
+            "line" : 69,
+            "column" : 55,
+            "source_fragment" : "hdr.gtpu"
+          }
+        }
+      ]
+    }
+  ],
+  "pipelines" : [
+    {
+      "name" : "ingress",
+      "id" : 0,
+      "source_info" : {
+        "filename" : "fabric.p4",
+        "line" : 33,
+        "column" : 8,
+        "source_fragment" : "FabricIngress"
+      },
+      "init_table" : "node_2",
+      "tables" : [
+        {
+          "name" : "tbl_act",
+          "id" : 0,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [36],
+          "actions" : ["act"],
+          "base_default_next" : null,
+          "next_tables" : {
+            "act" : null
+          },
+          "default_entry" : {
+            "action_id" : 36,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        },
+        {
+          "name" : "tbl_act_0",
+          "id" : 1,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [44],
+          "actions" : ["act_7"],
+          "base_default_next" : "node_5",
+          "next_tables" : {
+            "act_7" : "node_5"
+          },
+          "default_entry" : {
+            "action_id" : 44,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        },
+        {
+          "name" : "tbl_act_1",
+          "id" : 2,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [39],
+          "actions" : ["act_2"],
+          "base_default_next" : "spgw_ingress.s1u_filter_table",
+          "next_tables" : {
+            "act_2" : "spgw_ingress.s1u_filter_table"
+          },
+          "default_entry" : {
+            "action_id" : 39,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        },
+        {
+          "name" : "spgw_ingress.s1u_filter_table",
+          "id" : 3,
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 65,
+            "column" : 10,
+            "source_fragment" : "s1u_filter_table"
+          },
+          "key" : [
+            {
+              "match_type" : "exact",
+              "target" : ["spgw", "s1u_sgw_addr"],
+              "mask" : null
+            }
+          ],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [1],
+          "actions" : ["NoAction"],
+          "base_default_next" : null,
+          "next_tables" : {
+            "__HIT__" : "tbl_act_2",
+            "__MISS__" : "tbl_act_3"
+          },
+          "default_entry" : {
+            "action_id" : 1,
+            "action_const" : false,
+            "action_data" : [],
+            "action_entry_const" : false
+          }
+        },
+        {
+          "name" : "tbl_act_2",
+          "id" : 4,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [37],
+          "actions" : ["act_0"],
+          "base_default_next" : "node_10",
+          "next_tables" : {
+            "act_0" : "node_10"
+          },
+          "default_entry" : {
+            "action_id" : 37,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        },
+        {
+          "name" : "tbl_act_3",
+          "id" : 5,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [38],
+          "actions" : ["act_1"],
+          "base_default_next" : "node_10",
+          "next_tables" : {
+            "act_1" : "node_10"
+          },
+          "default_entry" : {
+            "action_id" : 38,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        },
+        {
+          "name" : "tbl_act_4",
+          "id" : 6,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [40],
+          "actions" : ["act_3"],
+          "base_default_next" : "node_17",
+          "next_tables" : {
+            "act_3" : "node_17"
+          },
+          "default_entry" : {
+            "action_id" : 40,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        },
+        {
+          "name" : "spgw_ingress.ue_filter_table",
+          "id" : 7,
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 55,
+            "column" : 10,
+            "source_fragment" : "ue_filter_table"
+          },
+          "key" : [
+            {
+              "match_type" : "lpm",
+              "target" : ["ipv4", "dst_addr"],
+              "mask" : null
+            }
+          ],
+          "match_type" : "lpm",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [0],
+          "actions" : ["NoAction"],
+          "base_default_next" : null,
+          "next_tables" : {
+            "__HIT__" : "tbl_act_5",
+            "__MISS__" : "tbl_act_6"
+          },
+          "default_entry" : {
+            "action_id" : 0,
+            "action_const" : false,
+            "action_data" : [],
+            "action_entry_const" : false
+          }
+        },
+        {
+          "name" : "tbl_act_5",
+          "id" : 8,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [41],
+          "actions" : ["act_4"],
+          "base_default_next" : "node_15",
+          "next_tables" : {
+            "act_4" : "node_15"
+          },
+          "default_entry" : {
+            "action_id" : 41,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        },
+        {
+          "name" : "tbl_act_6",
+          "id" : 9,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [42],
+          "actions" : ["act_5"],
+          "base_default_next" : "node_15",
+          "next_tables" : {
+            "act_5" : "node_15"
+          },
+          "default_entry" : {
+            "action_id" : 42,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        },
+        {
+          "name" : "tbl_act_7",
+          "id" : 10,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [43],
+          "actions" : ["act_6"],
+          "base_default_next" : "node_17",
+          "next_tables" : {
+            "act_6" : "node_17"
+          },
+          "default_entry" : {
+            "action_id" : 43,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        },
+        {
+          "name" : "tbl_act_8",
+          "id" : 11,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [45],
+          "actions" : ["act_8"],
+          "base_default_next" : "node_19",
+          "next_tables" : {
+            "act_8" : "node_19"
+          },
+          "default_entry" : {
+            "action_id" : 45,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        },
+        {
+          "name" : "tbl_spgw_ingress_gtpu_decap",
+          "id" : 12,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [14],
+          "actions" : ["spgw_ingress.gtpu_decap"],
+          "base_default_next" : "node_22",
+          "next_tables" : {
+            "spgw_ingress.gtpu_decap" : "node_22"
+          },
+          "default_entry" : {
+            "action_id" : 14,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        },
+        {
+          "name" : "spgw_ingress.dl_sess_lookup",
+          "id" : 13,
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 124,
+            "column" : 10,
+            "source_fragment" : "dl_sess_lookup"
+          },
+          "key" : [
+            {
+              "match_type" : "exact",
+              "target" : ["ipv4", "dst_addr"],
+              "mask" : null
+            }
+          ],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [15, 2],
+          "actions" : ["spgw_ingress.set_dl_sess_info", "NoAction"],
+          "base_default_next" : null,
+          "next_tables" : {
+            "__HIT__" : "tbl_act_9",
+            "__MISS__" : "tbl_act_10"
+          },
+          "default_entry" : {
+            "action_id" : 2,
+            "action_const" : false,
+            "action_data" : [],
+            "action_entry_const" : false
+          }
+        },
+        {
+          "name" : "tbl_act_9",
+          "id" : 14,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [46],
+          "actions" : ["act_9"],
+          "base_default_next" : "node_26",
+          "next_tables" : {
+            "act_9" : "node_26"
+          },
+          "default_entry" : {
+            "action_id" : 46,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        },
+        {
+          "name" : "tbl_act_10",
+          "id" : 15,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [47],
+          "actions" : ["act_10"],
+          "base_default_next" : "node_26",
+          "next_tables" : {
+            "act_10" : "node_26"
+          },
+          "default_entry" : {
+            "action_id" : 47,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        },
+        {
+          "name" : "tbl_spgw_ingress_drop_now",
+          "id" : 16,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [13],
+          "actions" : ["spgw_ingress.drop_now"],
+          "base_default_next" : "spgw_ingress.ue_cdr_table",
+          "next_tables" : {
+            "spgw_ingress.drop_now" : "spgw_ingress.ue_cdr_table"
+          },
+          "default_entry" : {
+            "action_id" : 13,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        },
+        {
+          "name" : "spgw_ingress.ue_cdr_table",
+          "id" : 17,
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 134,
+            "column" : 10,
+            "source_fragment" : "ue_cdr_table"
+          },
+          "key" : [
+            {
+              "match_type" : "exact",
+              "target" : ["ipv4", "dst_addr"],
+              "mask" : null
+            }
+          ],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : true,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [16, 3],
+          "actions" : ["spgw_ingress.update_ue_cdr", "NoAction"],
+          "base_default_next" : "filtering.ingress_port_vlan",
+          "next_tables" : {
+            "spgw_ingress.update_ue_cdr" : "filtering.ingress_port_vlan",
+            "NoAction" : "filtering.ingress_port_vlan"
+          },
+          "default_entry" : {
+            "action_id" : 3,
+            "action_const" : false,
+            "action_data" : [],
+            "action_entry_const" : false
+          }
+        },
+        {
+          "name" : "filtering.ingress_port_vlan",
+          "id" : 18,
+          "source_info" : {
+            "filename" : "include/control/filtering.p4",
+            "line" : 57,
+            "column" : 10,
+            "source_fragment" : "ingress_port_vlan"
+          },
+          "key" : [
+            {
+              "match_type" : "exact",
+              "target" : ["standard_metadata", "ingress_port"],
+              "mask" : null
+            },
+            {
+              "match_type" : "exact",
+              "target" : ["vlan_tag", "$valid$"],
+              "mask" : null
+            },
+            {
+              "match_type" : "ternary",
+              "target" : ["vlan_tag", "vlan_id"],
+              "mask" : null
+            }
+          ],
+          "match_type" : "ternary",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : true,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [19, 18, 11, 17],
+          "actions" : ["filtering.push_internal_vlan", "filtering.set_vlan", "nop", "filtering.drop"],
+          "base_default_next" : "filtering.fwd_classifier",
+          "next_tables" : {
+            "filtering.push_internal_vlan" : "filtering.fwd_classifier",
+            "filtering.set_vlan" : "filtering.fwd_classifier",
+            "nop" : "filtering.fwd_classifier",
+            "filtering.drop" : "filtering.fwd_classifier"
+          },
+          "default_entry" : {
+            "action_id" : 11,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        },
+        {
+          "name" : "filtering.fwd_classifier",
+          "id" : 19,
+          "source_info" : {
+            "filename" : "include/control/filtering.p4",
+            "line" : 76,
+            "column" : 10,
+            "source_fragment" : "fwd_classifier"
+          },
+          "key" : [
+            {
+              "match_type" : "exact",
+              "target" : ["standard_metadata", "ingress_port"],
+              "mask" : null
+            },
+            {
+              "match_type" : "exact",
+              "target" : ["ethernet", "dst_addr"],
+              "mask" : null
+            },
+            {
+              "match_type" : "exact",
+              "target" : ["scalars", "fabric_metadata_t.original_ether_type"],
+              "mask" : null
+            }
+          ],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : true,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [20],
+          "actions" : ["filtering.set_forwarding_type"],
+          "base_default_next" : "node_31",
+          "next_tables" : {
+            "filtering.set_forwarding_type" : "node_31"
+          },
+          "default_entry" : {
+            "action_id" : 20,
+            "action_const" : true,
+            "action_data" : ["0x0"],
+            "action_entry_const" : true
+          }
+        },
+        {
+          "name" : "forwarding.bridging",
+          "id" : 20,
+          "source_info" : {
+            "filename" : "include/control/forwarding.p4",
+            "line" : 53,
+            "column" : 10,
+            "source_fragment" : "bridging"
+          },
+          "key" : [
+            {
+              "match_type" : "exact",
+              "target" : ["vlan_tag", "vlan_id"],
+              "mask" : null
+            },
+            {
+              "match_type" : "ternary",
+              "target" : ["ethernet", "dst_addr"],
+              "mask" : null
+            }
+          ],
+          "match_type" : "ternary",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : true,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [22, 4],
+          "actions" : ["forwarding.set_next_id", "NoAction"],
+          "base_default_next" : "forwarding.acl",
+          "next_tables" : {
+            "forwarding.set_next_id" : "forwarding.acl",
+            "NoAction" : "forwarding.acl"
+          },
+          "default_entry" : {
+            "action_id" : 4,
+            "action_const" : false,
+            "action_data" : [],
+            "action_entry_const" : false
+          }
+        },
+        {
+          "name" : "forwarding.mpls",
+          "id" : 21,
+          "source_info" : {
+            "filename" : "include/control/forwarding.p4",
+            "line" : 65,
+            "column" : 10,
+            "source_fragment" : "mpls"
+          },
+          "key" : [
+            {
+              "match_type" : "exact",
+              "target" : ["mpls", "label"],
+              "mask" : null
+            }
+          ],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : true,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [26, 5],
+          "actions" : ["forwarding.pop_mpls_and_next", "NoAction"],
+          "base_default_next" : "tbl_act_11",
+          "next_tables" : {
+            "forwarding.pop_mpls_and_next" : "tbl_act_11",
+            "NoAction" : "tbl_act_11"
+          },
+          "default_entry" : {
+            "action_id" : 5,
+            "action_const" : false,
+            "action_data" : [],
+            "action_entry_const" : false
+          }
+        },
+        {
+          "name" : "tbl_act_11",
+          "id" : 22,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [48],
+          "actions" : ["act_11"],
+          "base_default_next" : "forwarding.acl",
+          "next_tables" : {
+            "act_11" : "forwarding.acl"
+          },
+          "default_entry" : {
+            "action_id" : 48,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        },
+        {
+          "name" : "forwarding.unicast_v4",
+          "id" : 23,
+          "source_info" : {
+            "filename" : "include/control/forwarding.p4",
+            "line" : 76,
+            "column" : 10,
+            "source_fragment" : "unicast_v4"
+          },
+          "key" : [
+            {
+              "match_type" : "lpm",
+              "target" : ["ipv4", "dst_addr"],
+              "mask" : null
+            }
+          ],
+          "match_type" : "lpm",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : true,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [23, 6],
+          "actions" : ["forwarding.set_next_id", "NoAction"],
+          "base_default_next" : "forwarding.acl",
+          "next_tables" : {
+            "forwarding.set_next_id" : "forwarding.acl",
+            "NoAction" : "forwarding.acl"
+          },
+          "default_entry" : {
+            "action_id" : 6,
+            "action_const" : false,
+            "action_data" : [],
+            "action_entry_const" : false
+          }
+        },
+        {
+          "name" : "forwarding.multicast_v4",
+          "id" : 24,
+          "source_info" : {
+            "filename" : "include/control/forwarding.p4",
+            "line" : 87,
+            "column" : 10,
+            "source_fragment" : "multicast_v4"
+          },
+          "key" : [
+            {
+              "match_type" : "exact",
+              "target" : ["vlan_tag", "vlan_id"],
+              "mask" : null
+            },
+            {
+              "match_type" : "lpm",
+              "target" : ["ipv4", "dst_addr"],
+              "mask" : null
+            }
+          ],
+          "match_type" : "lpm",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : true,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [24, 7],
+          "actions" : ["forwarding.set_next_id", "NoAction"],
+          "base_default_next" : "forwarding.acl",
+          "next_tables" : {
+            "forwarding.set_next_id" : "forwarding.acl",
+            "NoAction" : "forwarding.acl"
+          },
+          "default_entry" : {
+            "action_id" : 7,
+            "action_const" : false,
+            "action_data" : [],
+            "action_entry_const" : false
+          }
+        },
+        {
+          "name" : "forwarding.acl",
+          "id" : 25,
+          "source_info" : {
+            "filename" : "include/control/forwarding.p4",
+            "line" : 127,
+            "column" : 10,
+            "source_fragment" : "acl"
+          },
+          "key" : [
+            {
+              "match_type" : "ternary",
+              "target" : ["standard_metadata", "ingress_port"],
+              "mask" : null
+            },
+            {
+              "match_type" : "ternary",
+              "target" : ["scalars", "fabric_metadata_t.ip_proto"],
+              "mask" : null
+            },
+            {
+              "match_type" : "ternary",
+              "target" : ["scalars", "fabric_metadata_t.l4_src_port"],
+              "mask" : null
+            },
+            {
+              "match_type" : "ternary",
+              "target" : ["scalars", "fabric_metadata_t.l4_dst_port"],
+              "mask" : null
+            },
+            {
+              "match_type" : "ternary",
+              "target" : ["scalars", "fabric_metadata_t.original_ether_type"],
+              "mask" : null
+            },
+            {
+              "match_type" : "ternary",
+              "target" : ["ethernet", "dst_addr"],
+              "mask" : null
+            },
+            {
+              "match_type" : "ternary",
+              "target" : ["ethernet", "src_addr"],
+              "mask" : null
+            },
+            {
+              "match_type" : "ternary",
+              "target" : ["vlan_tag", "vlan_id"],
+              "mask" : null
+            },
+            {
+              "match_type" : "ternary",
+              "target" : ["ipv4", "src_addr"],
+              "mask" : null
+            },
+            {
+              "match_type" : "ternary",
+              "target" : ["ipv4", "dst_addr"],
+              "mask" : null
+            },
+            {
+              "match_type" : "ternary",
+              "target" : ["icmp", "icmp_type"],
+              "mask" : null
+            },
+            {
+              "match_type" : "ternary",
+              "target" : ["icmp", "icmp_code"],
+              "mask" : null
+            }
+          ],
+          "match_type" : "ternary",
+          "type" : "simple",
+          "max_size" : 256,
+          "with_counters" : true,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [25, 27, 21, 12],
+          "actions" : ["forwarding.set_next_id", "forwarding.duplicate_to_controller", "forwarding.drop", "nop"],
+          "base_default_next" : "next.simple",
+          "next_tables" : {
+            "forwarding.set_next_id" : "next.simple",
+            "forwarding.duplicate_to_controller" : "next.simple",
+            "forwarding.drop" : "next.simple",
+            "nop" : "next.simple"
+          },
+          "default_entry" : {
+            "action_id" : 12,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        },
+        {
+          "name" : "next.simple",
+          "id" : 26,
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 89,
+            "column" : 10,
+            "source_fragment" : "simple"
+          },
+          "key" : [
+            {
+              "match_type" : "exact",
+              "target" : ["scalars", "fabric_metadata_t.next_id"],
+              "mask" : null
+            }
+          ],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : true,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [28, 29, 30, 33, 8],
+          "actions" : ["next.output", "next.set_vlan_output", "next.l3_routing", "next.mpls_routing_v4", "NoAction"],
+          "base_default_next" : null,
+          "next_tables" : {
+            "__HIT__" : "tbl_act_12",
+            "__MISS__" : "tbl_act_13"
+          },
+          "default_entry" : {
+            "action_id" : 8,
+            "action_const" : false,
+            "action_data" : [],
+            "action_entry_const" : false
+          }
+        },
+        {
+          "name" : "tbl_act_12",
+          "id" : 27,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [49],
+          "actions" : ["act_12"],
+          "base_default_next" : "node_44",
+          "next_tables" : {
+            "act_12" : "node_44"
+          },
+          "default_entry" : {
+            "action_id" : 49,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        },
+        {
+          "name" : "tbl_act_13",
+          "id" : 28,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [50],
+          "actions" : ["act_13"],
+          "base_default_next" : "node_44",
+          "next_tables" : {
+            "act_13" : "node_44"
+          },
+          "default_entry" : {
+            "action_id" : 50,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        },
+        {
+          "name" : "tbl_act_14",
+          "id" : 29,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [51],
+          "actions" : ["act_14"],
+          "base_default_next" : "next.hashed",
+          "next_tables" : {
+            "act_14" : "next.hashed"
+          },
+          "default_entry" : {
+            "action_id" : 51,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        },
+        {
+          "name" : "next.hashed",
+          "id" : 30,
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 103,
+            "column" : 10,
+            "source_fragment" : "hashed"
+          },
+          "key" : [
+            {
+              "match_type" : "exact",
+              "target" : ["scalars", "fabric_metadata_t.next_id"],
+              "mask" : null
+            }
+          ],
+          "match_type" : "exact",
+          "type" : "indirect_ws",
+          "action_profile" : "next.ecmp_selector",
+          "max_size" : 1024,
+          "with_counters" : true,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [31, 34, 35, 9],
+          "actions" : ["next.l3_routing", "next.mpls_routing_v4", "next.mpls_routing_v6", "NoAction"],
+          "base_default_next" : "next.broadcast",
+          "next_tables" : {
+            "next.l3_routing" : "next.broadcast",
+            "next.mpls_routing_v4" : "next.broadcast",
+            "next.mpls_routing_v6" : "next.broadcast",
+            "NoAction" : "next.broadcast"
+          }
+        },
+        {
+          "name" : "next.broadcast",
+          "id" : 31,
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 126,
+            "column" : 10,
+            "source_fragment" : "broadcast"
+          },
+          "key" : [
+            {
+              "match_type" : "exact",
+              "target" : ["scalars", "fabric_metadata_t.next_id"],
+              "mask" : null
+            }
+          ],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : true,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [32, 10],
+          "actions" : ["next.set_mcast_group", "NoAction"],
+          "base_default_next" : "node_50",
+          "next_tables" : {
+            "next.set_mcast_group" : "node_50",
+            "NoAction" : "node_50"
+          },
+          "default_entry" : {
+            "action_id" : 10,
+            "action_const" : false,
+            "action_data" : [],
+            "action_entry_const" : false
+          }
+        },
+        {
+          "name" : "tbl_act_15",
+          "id" : 32,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [52],
+          "actions" : ["act_15"],
+          "base_default_next" : "node_52",
+          "next_tables" : {
+            "act_15" : "node_52"
+          },
+          "default_entry" : {
+            "action_id" : 52,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        },
+        {
+          "name" : "tbl_act_16",
+          "id" : 33,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [53],
+          "actions" : ["act_16"],
+          "base_default_next" : "node_54",
+          "next_tables" : {
+            "act_16" : "node_54"
+          },
+          "default_entry" : {
+            "action_id" : 53,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        },
+        {
+          "name" : "tbl_act_17",
+          "id" : 34,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [54],
+          "actions" : ["act_17"],
+          "base_default_next" : null,
+          "next_tables" : {
+            "act_17" : null
+          },
+          "default_entry" : {
+            "action_id" : 54,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        }
+      ],
+      "action_profiles" : [
+        {
+          "name" : "next.ecmp_selector",
+          "id" : 0,
+          "max_size" : 64,
+          "selector" : {
+            "algo" : "crc16",
+            "input" : [
+              {
+                "type" : "field",
+                "value" : ["ethernet", "dst_addr"]
+              },
+              {
+                "type" : "field",
+                "value" : ["ethernet", "src_addr"]
+              },
+              {
+                "type" : "field",
+                "value" : ["scalars", "fabric_metadata_t.ip_proto"]
+              },
+              {
+                "type" : "field",
+                "value" : ["scalars", "fabric_metadata_t.l4_src_port"]
+              },
+              {
+                "type" : "field",
+                "value" : ["scalars", "fabric_metadata_t.l4_dst_port"]
+              }
+            ]
+          }
+        }
+      ],
+      "conditionals" : [
+        {
+          "name" : "node_2",
+          "id" : 0,
+          "source_info" : {
+            "filename" : "include/control/packetio.p4",
+            "line" : 25,
+            "column" : 12,
+            "source_fragment" : "hdr.packet_out.isValid()"
+          },
+          "expression" : {
+            "type" : "expression",
+            "value" : {
+              "op" : "d2b",
+              "left" : null,
+              "right" : {
+                "type" : "field",
+                "value" : ["packet_out", "$valid$"]
+              }
+            }
+          },
+          "true_next" : "tbl_act",
+          "false_next" : "tbl_act_0"
+        },
+        {
+          "name" : "node_5",
+          "id" : 1,
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 147,
+            "column" : 12,
+            "source_fragment" : "gtpu.isValid()"
+          },
+          "expression" : {
+            "type" : "expression",
+            "value" : {
+              "op" : "d2b",
+              "left" : null,
+              "right" : {
+                "type" : "field",
+                "value" : ["gtpu", "$valid$"]
+              }
+            }
+          },
+          "true_next" : "tbl_act_1",
+          "false_next" : "spgw_ingress.ue_filter_table"
+        },
+        {
+          "name" : "node_10",
+          "id" : 2,
+          "expression" : {
+            "type" : "expression",
+            "value" : {
+              "op" : "d2b",
+              "left" : null,
+              "right" : {
+                "type" : "field",
+                "value" : ["scalars", "spgw_ingress_tmp_2"]
+              }
+            }
+          },
+          "true_next" : "tbl_act_4",
+          "false_next" : "node_17"
+        },
+        {
+          "name" : "node_15",
+          "id" : 3,
+          "expression" : {
+            "type" : "expression",
+            "value" : {
+              "op" : "d2b",
+              "left" : null,
+              "right" : {
+                "type" : "field",
+                "value" : ["scalars", "spgw_ingress_tmp_3"]
+              }
+            }
+          },
+          "true_next" : "tbl_act_7",
+          "false_next" : "node_17"
+        },
+        {
+          "name" : "node_17",
+          "id" : 4,
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 167,
+            "column" : 12,
+            "source_fragment" : "!spgw_meta.do_spgw"
+          },
+          "expression" : {
+            "type" : "expression",
+            "value" : {
+              "op" : "not",
+              "left" : null,
+              "right" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "d2b",
+                  "left" : null,
+                  "right" : {
+                    "type" : "field",
+                    "value" : ["spgw", "do_spgw"]
+                  }
+                }
+              }
+            }
+          },
+          "true_next" : "tbl_act_8",
+          "false_next" : "node_19"
+        },
+        {
+          "name" : "node_19",
+          "id" : 5,
+          "expression" : {
+            "type" : "expression",
+            "value" : {
+              "op" : "not",
+              "left" : null,
+              "right" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "d2b",
+                  "left" : null,
+                  "right" : {
+                    "type" : "field",
+                    "value" : ["scalars", "spgw_ingress_hasReturned_0"]
+                  }
+                }
+              }
+            }
+          },
+          "true_next" : "node_20",
+          "false_next" : "filtering.ingress_port_vlan"
+        },
+        {
+          "name" : "node_20",
+          "id" : 6,
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 172,
+            "column" : 12,
+            "source_fragment" : "spgw_meta.direction == DIR_UPLINK"
+          },
+          "expression" : {
+            "type" : "expression",
+            "value" : {
+              "op" : "==",
+              "left" : {
+                "type" : "field",
+                "value" : ["spgw", "direction"]
+              },
+              "right" : {
+                "type" : "hexstr",
+                "value" : "0x00"
+              }
+            }
+          },
+          "true_next" : "tbl_spgw_ingress_gtpu_decap",
+          "false_next" : "node_22"
+        },
+        {
+          "name" : "node_22",
+          "id" : 7,
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 189,
+            "column" : 12,
+            "source_fragment" : "spgw_meta.direction == DIR_DOWNLINK"
+          },
+          "expression" : {
+            "type" : "expression",
+            "value" : {
+              "op" : "==",
+              "left" : {
+                "type" : "field",
+                "value" : ["spgw", "direction"]
+              },
+              "right" : {
+                "type" : "hexstr",
+                "value" : "0x01"
+              }
+            }
+          },
+          "true_next" : "spgw_ingress.dl_sess_lookup",
+          "false_next" : "filtering.ingress_port_vlan"
+        },
+        {
+          "name" : "node_26",
+          "id" : 8,
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 190,
+            "column" : 16,
+            "source_fragment" : "!dl_sess_lookup.apply().hit"
+          },
+          "expression" : {
+            "type" : "expression",
+            "value" : {
+              "op" : "not",
+              "left" : null,
+              "right" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "d2b",
+                  "left" : null,
+                  "right" : {
+                    "type" : "field",
+                    "value" : ["scalars", "spgw_ingress_tmp_4"]
+                  }
+                }
+              }
+            }
+          },
+          "true_next" : "tbl_spgw_ingress_drop_now",
+          "false_next" : "spgw_ingress.ue_cdr_table"
+        },
+        {
+          "name" : "node_31",
+          "id" : 9,
+          "source_info" : {
+            "filename" : "include/control/forwarding.p4",
+            "line" : 157,
+            "column" : 11,
+            "source_fragment" : "fabric_metadata.fwd_type == FWD_BRIDGING"
+          },
+          "expression" : {
+            "type" : "expression",
+            "value" : {
+              "op" : "==",
+              "left" : {
+                "type" : "field",
+                "value" : ["scalars", "fabric_metadata_t.fwd_type"]
+              },
+              "right" : {
+                "type" : "hexstr",
+                "value" : "0x00"
+              }
+            }
+          },
+          "true_next" : "forwarding.bridging",
+          "false_next" : "node_33"
+        },
+        {
+          "name" : "node_33",
+          "id" : 10,
+          "source_info" : {
+            "filename" : "include/control/forwarding.p4",
+            "line" : 158,
+            "column" : 17,
+            "source_fragment" : "fabric_metadata.fwd_type == FWD_MPLS"
+          },
+          "expression" : {
+            "type" : "expression",
+            "value" : {
+              "op" : "==",
+              "left" : {
+                "type" : "field",
+                "value" : ["scalars", "fabric_metadata_t.fwd_type"]
+              },
+              "right" : {
+                "type" : "hexstr",
+                "value" : "0x01"
+              }
+            }
+          },
+          "true_next" : "forwarding.mpls",
+          "false_next" : "node_36"
+        },
+        {
+          "name" : "node_36",
+          "id" : 11,
+          "source_info" : {
+            "filename" : "include/control/forwarding.p4",
+            "line" : 165,
+            "column" : 17,
+            "source_fragment" : "fabric_metadata.fwd_type == FWD_IPV4_UNICAST"
+          },
+          "expression" : {
+            "type" : "expression",
+            "value" : {
+              "op" : "==",
+              "left" : {
+                "type" : "field",
+                "value" : ["scalars", "fabric_metadata_t.fwd_type"]
+              },
+              "right" : {
+                "type" : "hexstr",
+                "value" : "0x02"
+              }
+            }
+          },
+          "true_next" : "forwarding.unicast_v4",
+          "false_next" : "node_38"
+        },
+        {
+          "name" : "node_38",
+          "id" : 12,
+          "source_info" : {
+            "filename" : "include/control/forwarding.p4",
+            "line" : 166,
+            "column" : 17,
+            "source_fragment" : "fabric_metadata.fwd_type == FWD_IPV4_MULTICAST"
+          },
+          "expression" : {
+            "type" : "expression",
+            "value" : {
+              "op" : "==",
+              "left" : {
+                "type" : "field",
+                "value" : ["scalars", "fabric_metadata_t.fwd_type"]
+              },
+              "right" : {
+                "type" : "hexstr",
+                "value" : "0x03"
+              }
+            }
+          },
+          "true_next" : "forwarding.multicast_v4",
+          "false_next" : "forwarding.acl"
+        },
+        {
+          "name" : "node_44",
+          "id" : 13,
+          "expression" : {
+            "type" : "expression",
+            "value" : {
+              "op" : "d2b",
+              "left" : null,
+              "right" : {
+                "type" : "field",
+                "value" : ["scalars", "next_tmp_0"]
+              }
+            }
+          },
+          "true_next" : "node_45",
+          "false_next" : "next.hashed"
+        },
+        {
+          "name" : "node_45",
+          "id" : 14,
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 138,
+            "column" : 16,
+            "source_fragment" : "!hdr.mpls.isValid()"
+          },
+          "expression" : {
+            "type" : "expression",
+            "value" : {
+              "op" : "not",
+              "left" : null,
+              "right" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "d2b",
+                  "left" : null,
+                  "right" : {
+                    "type" : "field",
+                    "value" : ["mpls", "$valid$"]
+                  }
+                }
+              }
+            }
+          },
+          "true_next" : "node_46",
+          "false_next" : "next.hashed"
+        },
+        {
+          "name" : "node_46",
+          "id" : 15,
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 139,
+            "column" : 19,
+            "source_fragment" : "hdr.ipv4.isValid()"
+          },
+          "expression" : {
+            "type" : "expression",
+            "value" : {
+              "op" : "d2b",
+              "left" : null,
+              "right" : {
+                "type" : "field",
+                "value" : ["ipv4", "$valid$"]
+              }
+            }
+          },
+          "true_next" : "tbl_act_14",
+          "false_next" : "next.hashed"
+        },
+        {
+          "name" : "node_50",
+          "id" : 16,
+          "source_info" : {
+            "filename" : "include/control/port_counter.p4",
+            "line" : 27,
+            "column" : 12,
+            "source_fragment" : "standard_metadata.egress_spec < 511"
+          },
+          "expression" : {
+            "type" : "expression",
+            "value" : {
+              "op" : "<",
+              "left" : {
+                "type" : "field",
+                "value" : ["standard_metadata", "egress_spec"]
+              },
+              "right" : {
+                "type" : "hexstr",
+                "value" : "0x01ff"
+              }
+            }
+          },
+          "true_next" : "tbl_act_15",
+          "false_next" : "node_52"
+        },
+        {
+          "name" : "node_52",
+          "id" : 17,
+          "source_info" : {
+            "filename" : "include/control/port_counter.p4",
+            "line" : 30,
+            "column" : 12,
+            "source_fragment" : "standard_metadata.ingress_port < 511"
+          },
+          "expression" : {
+            "type" : "expression",
+            "value" : {
+              "op" : "<",
+              "left" : {
+                "type" : "field",
+                "value" : ["standard_metadata", "ingress_port"]
+              },
+              "right" : {
+                "type" : "hexstr",
+                "value" : "0x01ff"
+              }
+            }
+          },
+          "true_next" : "tbl_act_16",
+          "false_next" : "node_54"
+        },
+        {
+          "name" : "node_54",
+          "id" : 18,
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 161,
+            "column" : 12,
+            "source_fragment" : "fabric_metadata.pop_vlan_at_egress"
+          },
+          "expression" : {
+            "type" : "expression",
+            "value" : {
+              "op" : "d2b",
+              "left" : null,
+              "right" : {
+                "type" : "field",
+                "value" : ["scalars", "fabric_metadata_t.pop_vlan_at_egress"]
+              }
+            }
+          },
+          "false_next" : null,
+          "true_next" : "tbl_act_17"
+        }
+      ]
+    },
+    {
+      "name" : "egress",
+      "id" : 1,
+      "source_info" : {
+        "filename" : "fabric.p4",
+        "line" : 62,
+        "column" : 8,
+        "source_fragment" : "FabricEgress"
+      },
+      "init_table" : "node_58",
+      "tables" : [
+        {
+          "name" : "tbl_act_18",
+          "id" : 35,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [56],
+          "actions" : ["act_18"],
+          "base_default_next" : "tbl_act_19",
+          "next_tables" : {
+            "act_18" : "tbl_act_19"
+          },
+          "default_entry" : {
+            "action_id" : 56,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        },
+        {
+          "name" : "tbl_act_19",
+          "id" : 36,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [57],
+          "actions" : ["act_19"],
+          "base_default_next" : "node_61",
+          "next_tables" : {
+            "act_19" : "node_61"
+          },
+          "default_entry" : {
+            "action_id" : 57,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        },
+        {
+          "name" : "tbl_spgw_egress_gtpu_encap",
+          "id" : 37,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [55],
+          "actions" : ["spgw_egress.gtpu_encap"],
+          "base_default_next" : null,
+          "next_tables" : {
+            "spgw_egress.gtpu_encap" : null
+          },
+          "default_entry" : {
+            "action_id" : 55,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        }
+      ],
+      "action_profiles" : [],
+      "conditionals" : [
+        {
+          "name" : "node_58",
+          "id" : 19,
+          "source_info" : {
+            "filename" : "include/control/packetio.p4",
+            "line" : 38,
+            "column" : 12,
+            "source_fragment" : "standard_metadata.egress_port == CPU_PORT"
+          },
+          "expression" : {
+            "type" : "expression",
+            "value" : {
+              "op" : "==",
+              "left" : {
+                "type" : "field",
+                "value" : ["standard_metadata", "egress_port"]
+              },
+              "right" : {
+                "type" : "hexstr",
+                "value" : "0x00ff"
+              }
+            }
+          },
+          "true_next" : "tbl_act_18",
+          "false_next" : "tbl_act_19"
+        },
+        {
+          "name" : "node_61",
+          "id" : 20,
+          "source_info" : {
+            "filename" : "include/spgw.p4",
+            "line" : 245,
+            "column" : 12,
+            "source_fragment" : "spgw_meta.do_spgw && spgw_meta.direction == DIR_DOWNLINK"
+          },
+          "expression" : {
+            "type" : "expression",
+            "value" : {
+              "op" : "and",
+              "left" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "d2b",
+                  "left" : null,
+                  "right" : {
+                    "type" : "field",
+                    "value" : ["spgw", "do_spgw"]
+                  }
+                }
+              },
+              "right" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "==",
+                  "left" : {
+                    "type" : "field",
+                    "value" : ["spgw", "direction"]
+                  },
+                  "right" : {
+                    "type" : "hexstr",
+                    "value" : "0x01"
+                  }
+                }
+              }
+            }
+          },
+          "false_next" : null,
+          "true_next" : "tbl_spgw_egress_gtpu_encap"
+        }
+      ]
+    }
+  ],
+  "checksums" : [
+    {
+      "name" : "cksum",
+      "id" : 0,
+      "target" : ["ipv4", "hdr_checksum"],
+      "type" : "generic",
+      "calculation" : "calc",
+      "if_cond" : {
+        "type" : "expression",
+        "value" : {
+          "op" : "d2b",
+          "left" : null,
+          "right" : {
+            "type" : "field",
+            "value" : ["ipv4", "$valid$"]
+          }
+        }
+      }
+    },
+    {
+      "name" : "cksum_0",
+      "id" : 1,
+      "target" : ["ipv4", "hdr_checksum"],
+      "type" : "generic",
+      "calculation" : "calc_0",
+      "if_cond" : {
+        "type" : "expression",
+        "value" : {
+          "op" : "d2b",
+          "left" : null,
+          "right" : {
+            "type" : "field",
+            "value" : ["ipv4", "$valid$"]
+          }
+        }
+      }
+    },
+    {
+      "name" : "cksum_1",
+      "id" : 2,
+      "target" : ["gtpu_ipv4", "hdr_checksum"],
+      "type" : "generic",
+      "calculation" : "calc_1",
+      "if_cond" : {
+        "type" : "expression",
+        "value" : {
+          "op" : "d2b",
+          "left" : null,
+          "right" : {
+            "type" : "field",
+            "value" : ["gtpu_ipv4", "$valid$"]
+          }
+        }
+      }
+    },
+    {
+      "name" : "cksum_2",
+      "id" : 3,
+      "target" : ["gtpu_udp", "checksum"],
+      "type" : "generic",
+      "calculation" : "calc_2",
+      "if_cond" : {
+        "type" : "expression",
+        "value" : {
+          "op" : "d2b",
+          "left" : null,
+          "right" : {
+            "type" : "field",
+            "value" : ["gtpu_udp", "$valid$"]
+          }
+        }
+      }
+    }
+  ],
+  "force_arith" : [],
+  "extern_instances" : [],
+  "field_aliases" : [
+    [
+      "queueing_metadata.enq_timestamp",
+      ["standard_metadata", "enq_timestamp"]
+    ],
+    [
+      "queueing_metadata.enq_qdepth",
+      ["standard_metadata", "enq_qdepth"]
+    ],
+    [
+      "queueing_metadata.deq_timedelta",
+      ["standard_metadata", "deq_timedelta"]
+    ],
+    [
+      "queueing_metadata.deq_qdepth",
+      ["standard_metadata", "deq_qdepth"]
+    ],
+    [
+      "intrinsic_metadata.ingress_global_timestamp",
+      ["standard_metadata", "ingress_global_timestamp"]
+    ],
+    [
+      "intrinsic_metadata.lf_field_list",
+      ["standard_metadata", "lf_field_list"]
+    ],
+    [
+      "intrinsic_metadata.mcast_grp",
+      ["standard_metadata", "mcast_grp"]
+    ],
+    [
+      "intrinsic_metadata.resubmit_flag",
+      ["standard_metadata", "resubmit_flag"]
+    ],
+    [
+      "intrinsic_metadata.egress_rid",
+      ["standard_metadata", "egress_rid"]
+    ]
+  ]
+}
\ No newline at end of file
diff --git a/pipelines/fabric/src/main/resources/p4c-out/bmv2/fabric-spgw.p4info b/pipelines/fabric/src/main/resources/p4c-out/bmv2/fabric-spgw.p4info
new file mode 100644
index 0000000..73ad2f9
--- /dev/null
+++ b/pipelines/fabric/src/main/resources/p4c-out/bmv2/fabric-spgw.p4info
@@ -0,0 +1,878 @@
+tables {
+  preamble {
+    id: 33574964
+    name: "spgw_ingress.ue_filter_table"
+    alias: "ue_filter_table"
+  }
+  match_fields {
+    id: 1
+    name: "ipv4.dst_addr"
+    bitwidth: 32
+    match_type: LPM
+  }
+  action_refs {
+    id: 16800567
+  }
+  size: 1024
+}
+tables {
+  preamble {
+    id: 33570382
+    name: "spgw_ingress.s1u_filter_table"
+    alias: "s1u_filter_table"
+  }
+  match_fields {
+    id: 1
+    name: "spgw_meta.s1u_sgw_addr"
+    bitwidth: 32
+    match_type: EXACT
+  }
+  action_refs {
+    id: 16800567
+  }
+  size: 1024
+}
+tables {
+  preamble {
+    id: 33590421
+    name: "spgw_ingress.dl_sess_lookup"
+    alias: "dl_sess_lookup"
+  }
+  match_fields {
+    id: 1
+    name: "ipv4.dst_addr"
+    bitwidth: 32
+    match_type: EXACT
+  }
+  action_refs {
+    id: 16784665
+  }
+  action_refs {
+    id: 16800567
+    annotations: "@defaultonly()"
+  }
+  size: 1024
+}
+tables {
+  preamble {
+    id: 33594626
+    name: "spgw_ingress.ue_cdr_table"
+    alias: "ue_cdr_table"
+  }
+  match_fields {
+    id: 1
+    name: "ipv4.dst_addr"
+    bitwidth: 32
+    match_type: EXACT
+  }
+  action_refs {
+    id: 16800269
+  }
+  action_refs {
+    id: 16800567
+    annotations: "@defaultonly()"
+  }
+  direct_resource_ids: 302053240
+  size: 1024
+}
+tables {
+  preamble {
+    id: 33578399
+    name: "filtering.ingress_port_vlan"
+    alias: "ingress_port_vlan"
+  }
+  match_fields {
+    id: 1
+    name: "standard_metadata.ingress_port"
+    bitwidth: 9
+    match_type: EXACT
+  }
+  match_fields {
+    id: 2
+    name: "hdr.vlan_tag.is_valid"
+    bitwidth: 1
+    match_type: EXACT
+  }
+  match_fields {
+    id: 3
+    name: "hdr.vlan_tag.vlan_id"
+    bitwidth: 12
+    match_type: TERNARY
+  }
+  action_refs {
+    id: 16794505
+  }
+  action_refs {
+    id: 16782367
+  }
+  action_refs {
+    id: 16819938
+  }
+  action_refs {
+    id: 16826365
+  }
+  const_default_action_id: 16819938
+  direct_resource_ids: 302015144
+  size: 1024
+}
+tables {
+  preamble {
+    id: 33619540
+    name: "filtering.fwd_classifier"
+    alias: "fwd_classifier"
+  }
+  match_fields {
+    id: 1
+    name: "standard_metadata.ingress_port"
+    bitwidth: 9
+    match_type: EXACT
+  }
+  match_fields {
+    id: 2
+    name: "hdr.ethernet.dst_addr"
+    bitwidth: 48
+    match_type: EXACT
+  }
+  match_fields {
+    id: 3
+    name: "fabric_metadata.original_ether_type"
+    bitwidth: 16
+    match_type: EXACT
+  }
+  action_refs {
+    id: 16838162
+  }
+  const_default_action_id: 16838162
+  direct_resource_ids: 302033694
+  size: 1024
+}
+tables {
+  preamble {
+    id: 33569146
+    name: "forwarding.bridging"
+    alias: "bridging"
+  }
+  match_fields {
+    id: 1
+    name: "hdr.vlan_tag.vlan_id"
+    bitwidth: 12
+    match_type: EXACT
+  }
+  match_fields {
+    id: 2
+    name: "hdr.ethernet.dst_addr"
+    bitwidth: 48
+    match_type: TERNARY
+  }
+  action_refs {
+    id: 16829931
+  }
+  action_refs {
+    id: 16800567
+    annotations: "@defaultonly()"
+  }
+  direct_resource_ids: 302047449
+  size: 1024
+}
+tables {
+  preamble {
+    id: 33565386
+    name: "forwarding.mpls"
+    alias: "mpls"
+  }
+  match_fields {
+    id: 1
+    name: "hdr.mpls.label"
+    bitwidth: 20
+    match_type: EXACT
+  }
+  action_refs {
+    id: 16842717
+  }
+  action_refs {
+    id: 16800567
+    annotations: "@defaultonly()"
+  }
+  direct_resource_ids: 302001577
+  size: 1024
+}
+tables {
+  preamble {
+    id: 33589684
+    name: "forwarding.unicast_v4"
+    alias: "unicast_v4"
+  }
+  match_fields {
+    id: 1
+    name: "hdr.ipv4.dst_addr"
+    bitwidth: 32
+    match_type: LPM
+  }
+  action_refs {
+    id: 16829931
+  }
+  action_refs {
+    id: 16800567
+    annotations: "@defaultonly()"
+  }
+  direct_resource_ids: 302038636
+  size: 1024
+}
+tables {
+  preamble {
+    id: 33615204
+    name: "forwarding.multicast_v4"
+    alias: "multicast_v4"
+  }
+  match_fields {
+    id: 1
+    name: "hdr.vlan_tag.vlan_id"
+    bitwidth: 12
+    match_type: EXACT
+  }
+  match_fields {
+    id: 2
+    name: "hdr.ipv4.dst_addr"
+    bitwidth: 32
+    match_type: LPM
+  }
+  action_refs {
+    id: 16829931
+  }
+  action_refs {
+    id: 16800567
+    annotations: "@defaultonly()"
+  }
+  direct_resource_ids: 302009236
+  size: 1024
+}
+tables {
+  preamble {
+    id: 33587782
+    name: "forwarding.acl"
+    alias: "acl"
+  }
+  match_fields {
+    id: 1
+    name: "standard_metadata.ingress_port"
+    bitwidth: 9
+    match_type: TERNARY
+  }
+  match_fields {
+    id: 2
+    name: "fabric_metadata.ip_proto"
+    bitwidth: 8
+    match_type: TERNARY
+  }
+  match_fields {
+    id: 3
+    name: "fabric_metadata.l4_src_port"
+    bitwidth: 16
+    match_type: TERNARY
+  }
+  match_fields {
+    id: 4
+    name: "fabric_metadata.l4_dst_port"
+    bitwidth: 16
+    match_type: TERNARY
+  }
+  match_fields {
+    id: 5
+    name: "fabric_metadata.original_ether_type"
+    bitwidth: 16
+    match_type: TERNARY
+  }
+  match_fields {
+    id: 6
+    name: "hdr.ethernet.dst_addr"
+    bitwidth: 48
+    match_type: TERNARY
+  }
+  match_fields {
+    id: 7
+    name: "hdr.ethernet.src_addr"
+    bitwidth: 48
+    match_type: TERNARY
+  }
+  match_fields {
+    id: 8
+    name: "hdr.vlan_tag.vlan_id"
+    bitwidth: 12
+    match_type: TERNARY
+  }
+  match_fields {
+    id: 9
+    name: "hdr.ipv4.src_addr"
+    bitwidth: 32
+    match_type: TERNARY
+  }
+  match_fields {
+    id: 10
+    name: "hdr.ipv4.dst_addr"
+    bitwidth: 32
+    match_type: TERNARY
+  }
+  match_fields {
+    id: 11
+    name: "hdr.icmp.icmp_type"
+    bitwidth: 8
+    match_type: TERNARY
+  }
+  match_fields {
+    id: 12
+    name: "hdr.icmp.icmp_code"
+    bitwidth: 8
+    match_type: TERNARY
+  }
+  action_refs {
+    id: 16829931
+  }
+  action_refs {
+    id: 16805452
+  }
+  action_refs {
+    id: 16815978
+  }
+  action_refs {
+    id: 16819938
+  }
+  const_default_action_id: 16819938
+  direct_resource_ids: 302000008
+  size: 256
+}
+tables {
+  preamble {
+    id: 33615740
+    name: "next.simple"
+    alias: "simple"
+  }
+  match_fields {
+    id: 1
+    name: "fabric_metadata.next_id"
+    bitwidth: 32
+    match_type: EXACT
+  }
+  action_refs {
+    id: 16818315
+  }
+  action_refs {
+    id: 16837690
+  }
+  action_refs {
+    id: 16804266
+  }
+  action_refs {
+    id: 16841192
+  }
+  action_refs {
+    id: 16800567
+    annotations: "@defaultonly()"
+  }
+  direct_resource_ids: 301991179
+  size: 1024
+}
+tables {
+  preamble {
+    id: 33569488
+    name: "next.hashed"
+    alias: "hashed"
+  }
+  match_fields {
+    id: 1
+    name: "fabric_metadata.next_id"
+    bitwidth: 32
+    match_type: EXACT
+  }
+  action_refs {
+    id: 16804266
+  }
+  action_refs {
+    id: 16841192
+  }
+  action_refs {
+    id: 16788519
+  }
+  action_refs {
+    id: 16800567
+    annotations: "@defaultonly()"
+  }
+  implementation_id: 285225078
+  direct_resource_ids: 301993193
+  size: 1024
+}
+tables {
+  preamble {
+    id: 33608545
+    name: "next.broadcast"
+    alias: "broadcast"
+  }
+  match_fields {
+    id: 1
+    name: "fabric_metadata.next_id"
+    bitwidth: 32
+    match_type: EXACT
+  }
+  action_refs {
+    id: 16778974
+  }
+  action_refs {
+    id: 16800567
+    annotations: "@defaultonly()"
+  }
+  direct_resource_ids: 301995093
+  size: 1024
+}
+actions {
+  preamble {
+    id: 16800567
+    name: "NoAction"
+    alias: "NoAction"
+  }
+}
+actions {
+  preamble {
+    id: 16819938
+    name: "nop"
+    alias: "nop"
+  }
+}
+actions {
+  preamble {
+    id: 16840488
+    name: "spgw_ingress.drop_now"
+    alias: "drop_now"
+  }
+}
+actions {
+  preamble {
+    id: 16808035
+    name: "spgw_ingress.gtpu_decap"
+    alias: "gtpu_decap"
+  }
+}
+actions {
+  preamble {
+    id: 16784665
+    name: "spgw_ingress.set_dl_sess_info"
+    alias: "set_dl_sess_info"
+  }
+  params {
+    id: 1
+    name: "teid"
+    bitwidth: 32
+  }
+  params {
+    id: 2
+    name: "s1u_enb_addr"
+    bitwidth: 32
+  }
+  params {
+    id: 3
+    name: "s1u_sgw_addr"
+    bitwidth: 32
+  }
+}
+actions {
+  preamble {
+    id: 16800269
+    name: "spgw_ingress.update_ue_cdr"
+    alias: "update_ue_cdr"
+  }
+}
+actions {
+  preamble {
+    id: 16826365
+    name: "filtering.drop"
+    alias: "filtering.drop"
+  }
+}
+actions {
+  preamble {
+    id: 16782367
+    name: "filtering.set_vlan"
+    alias: "set_vlan"
+  }
+  params {
+    id: 1
+    name: "new_vlan_id"
+    bitwidth: 12
+  }
+}
+actions {
+  preamble {
+    id: 16794505
+    name: "filtering.push_internal_vlan"
+    alias: "push_internal_vlan"
+  }
+  params {
+    id: 1
+    name: "new_vlan_id"
+    bitwidth: 12
+  }
+}
+actions {
+  preamble {
+    id: 16838162
+    name: "filtering.set_forwarding_type"
+    alias: "set_forwarding_type"
+  }
+  params {
+    id: 1
+    name: "fwd_type"
+    bitwidth: 3
+  }
+}
+actions {
+  preamble {
+    id: 16815978
+    name: "forwarding.drop"
+    alias: "forwarding.drop"
+  }
+}
+actions {
+  preamble {
+    id: 16829931
+    name: "forwarding.set_next_id"
+    alias: "set_next_id"
+  }
+  params {
+    id: 1
+    name: "next_id"
+    bitwidth: 32
+  }
+}
+actions {
+  preamble {
+    id: 16842717
+    name: "forwarding.pop_mpls_and_next"
+    alias: "pop_mpls_and_next"
+  }
+  params {
+    id: 1
+    name: "next_id"
+    bitwidth: 32
+  }
+}
+actions {
+  preamble {
+    id: 16805452
+    name: "forwarding.duplicate_to_controller"
+    alias: "duplicate_to_controller"
+  }
+}
+actions {
+  preamble {
+    id: 16818315
+    name: "next.output"
+    alias: "output"
+  }
+  params {
+    id: 1
+    name: "port_num"
+    bitwidth: 9
+  }
+}
+actions {
+  preamble {
+    id: 16837690
+    name: "next.set_vlan_output"
+    alias: "set_vlan_output"
+  }
+  params {
+    id: 1
+    name: "new_vlan_id"
+    bitwidth: 12
+  }
+  params {
+    id: 2
+    name: "port_num"
+    bitwidth: 9
+  }
+}
+actions {
+  preamble {
+    id: 16804266
+    name: "next.l3_routing"
+    alias: "l3_routing"
+  }
+  params {
+    id: 1
+    name: "port_num"
+    bitwidth: 9
+  }
+  params {
+    id: 2
+    name: "smac"
+    bitwidth: 48
+  }
+  params {
+    id: 3
+    name: "dmac"
+    bitwidth: 48
+  }
+}
+actions {
+  preamble {
+    id: 16778974
+    name: "next.set_mcast_group"
+    alias: "set_mcast_group"
+  }
+  params {
+    id: 1
+    name: "gid"
+    bitwidth: 16
+  }
+  params {
+    id: 2
+    name: "smac"
+    bitwidth: 48
+  }
+}
+actions {
+  preamble {
+    id: 16841192
+    name: "next.mpls_routing_v4"
+    alias: "mpls_routing_v4"
+  }
+  params {
+    id: 1
+    name: "port_num"
+    bitwidth: 9
+  }
+  params {
+    id: 2
+    name: "smac"
+    bitwidth: 48
+  }
+  params {
+    id: 3
+    name: "dmac"
+    bitwidth: 48
+  }
+  params {
+    id: 4
+    name: "label"
+    bitwidth: 20
+  }
+}
+actions {
+  preamble {
+    id: 16788519
+    name: "next.mpls_routing_v6"
+    alias: "mpls_routing_v6"
+  }
+  params {
+    id: 1
+    name: "port_num"
+    bitwidth: 9
+  }
+  params {
+    id: 2
+    name: "smac"
+    bitwidth: 48
+  }
+  params {
+    id: 3
+    name: "dmac"
+    bitwidth: 48
+  }
+  params {
+    id: 4
+    name: "label"
+    bitwidth: 20
+  }
+}
+actions {
+  preamble {
+    id: 16839213
+    name: "spgw_egress.gtpu_encap"
+    alias: "gtpu_encap"
+  }
+}
+action_profiles {
+  preamble {
+    id: 285225078
+    name: "next.ecmp_selector"
+    alias: "ecmp_selector"
+  }
+  table_ids: 33569488
+  with_selector: true
+  size: 64
+}
+counters {
+  preamble {
+    id: 302025528
+    name: "port_counters_control.egress_port_counter"
+    alias: "egress_port_counter"
+  }
+  spec {
+    unit: PACKETS
+  }
+  size: 511
+}
+counters {
+  preamble {
+    id: 301999025
+    name: "port_counters_control.ingress_port_counter"
+    alias: "ingress_port_counter"
+  }
+  spec {
+    unit: PACKETS
+  }
+  size: 511
+}
+direct_counters {
+  preamble {
+    id: 302053240
+    name: "spgw_ingress.ue_counter"
+    alias: "ue_counter"
+  }
+  spec {
+    unit: BOTH
+  }
+  direct_table_id: 33594626
+}
+direct_counters {
+  preamble {
+    id: 302015144
+    name: "filtering.ingress_port_vlan_counter"
+    alias: "ingress_port_vlan_counter"
+  }
+  spec {
+    unit: BOTH
+  }
+  direct_table_id: 33578399
+}
+direct_counters {
+  preamble {
+    id: 302033694
+    name: "filtering.fwd_classifier_counter"
+    alias: "fwd_classifier_counter"
+  }
+  spec {
+    unit: BOTH
+  }
+  direct_table_id: 33619540
+}
+direct_counters {
+  preamble {
+    id: 302047449
+    name: "forwarding.bridging_counter"
+    alias: "bridging_counter"
+  }
+  spec {
+    unit: BOTH
+  }
+  direct_table_id: 33569146
+}
+direct_counters {
+  preamble {
+    id: 302001577
+    name: "forwarding.mpls_counter"
+    alias: "mpls_counter"
+  }
+  spec {
+    unit: BOTH
+  }
+  direct_table_id: 33565386
+}
+direct_counters {
+  preamble {
+    id: 302038636
+    name: "forwarding.unicast_v4_counter"
+    alias: "unicast_v4_counter"
+  }
+  spec {
+    unit: BOTH
+  }
+  direct_table_id: 33589684
+}
+direct_counters {
+  preamble {
+    id: 302009236
+    name: "forwarding.multicast_v4_counter"
+    alias: "multicast_v4_counter"
+  }
+  spec {
+    unit: BOTH
+  }
+  direct_table_id: 33615204
+}
+direct_counters {
+  preamble {
+    id: 302000008
+    name: "forwarding.acl_counter"
+    alias: "acl_counter"
+  }
+  spec {
+    unit: BOTH
+  }
+  direct_table_id: 33587782
+}
+direct_counters {
+  preamble {
+    id: 301991179
+    name: "next.simple_counter"
+    alias: "simple_counter"
+  }
+  spec {
+    unit: BOTH
+  }
+  direct_table_id: 33615740
+}
+direct_counters {
+  preamble {
+    id: 301993193
+    name: "next.hashed_counter"
+    alias: "hashed_counter"
+  }
+  spec {
+    unit: BOTH
+  }
+  direct_table_id: 33569488
+}
+direct_counters {
+  preamble {
+    id: 301995093
+    name: "next.broadcast_counter"
+    alias: "broadcast_counter"
+  }
+  spec {
+    unit: BOTH
+  }
+  direct_table_id: 33608545
+}
+controller_packet_metadata {
+  preamble {
+    id: 2868941301
+    name: "packet_in"
+    annotations: "@controller_header(\"packet_in\")"
+  }
+  metadata {
+    id: 1
+    name: "ingress_port"
+    bitwidth: 9
+  }
+  metadata {
+    id: 2
+    name: "_pad"
+    bitwidth: 7
+  }
+}
+controller_packet_metadata {
+  preamble {
+    id: 2868916615
+    name: "packet_out"
+    annotations: "@controller_header(\"packet_out\")"
+  }
+  metadata {
+    id: 1
+    name: "egress_port"
+    bitwidth: 9
+  }
+  metadata {
+    id: 2
+    name: "_pad"
+    bitwidth: 7
+  }
+}
diff --git a/pipelines/fabric/src/main/resources/p4c-out/bmv2/fabric.json b/pipelines/fabric/src/main/resources/p4c-out/bmv2/fabric.json
index 5c6e645..cdaefbc 100644
--- a/pipelines/fabric/src/main/resources/p4c-out/bmv2/fabric.json
+++ b/pipelines/fabric/src/main/resources/p4c-out/bmv2/fabric.json
@@ -206,71 +206,64 @@
       "pi_omit" : true
     },
     {
-      "name" : "inner_vlan_tag",
-      "id" : 4,
-      "header_type" : "vlan_tag_t",
-      "metadata" : false,
-      "pi_omit" : true
-    },
-    {
       "name" : "mpls",
-      "id" : 5,
+      "id" : 4,
       "header_type" : "mpls_t",
       "metadata" : false,
       "pi_omit" : true
     },
     {
       "name" : "ipv4",
-      "id" : 6,
+      "id" : 5,
       "header_type" : "ipv4_t",
       "metadata" : false,
       "pi_omit" : true
     },
     {
       "name" : "ipv6",
-      "id" : 7,
+      "id" : 6,
       "header_type" : "ipv6_t",
       "metadata" : false,
       "pi_omit" : true
     },
     {
       "name" : "arp",
-      "id" : 8,
+      "id" : 7,
       "header_type" : "arp_t",
       "metadata" : false,
       "pi_omit" : true
     },
     {
       "name" : "tcp",
-      "id" : 9,
+      "id" : 8,
       "header_type" : "tcp_t",
       "metadata" : false,
       "pi_omit" : true
     },
     {
       "name" : "udp",
-      "id" : 10,
+      "id" : 9,
       "header_type" : "udp_t",
       "metadata" : false,
       "pi_omit" : true
     },
     {
       "name" : "icmp",
-      "id" : 11,
+      "id" : 10,
       "header_type" : "icmp_t",
       "metadata" : false,
       "pi_omit" : true
     },
     {
       "name" : "packet_out",
-      "id" : 12,
+      "id" : 11,
       "header_type" : "packet_out_header_t",
       "metadata" : false,
       "pi_omit" : true
     },
     {
       "name" : "packet_in",
-      "id" : 13,
+      "id" : 12,
       "header_type" : "packet_in_header_t",
       "metadata" : false,
       "pi_omit" : true
@@ -282,12 +275,12 @@
   "header_union_stacks" : [],
   "field_lists" : [],
   "errors" : [
-    ["NoError", 0],
-    ["PacketTooShort", 1],
-    ["NoMatch", 2],
-    ["StackOutOfBounds", 3],
-    ["HeaderTooShort", 4],
-    ["ParserTimeout", 5]
+    ["NoError", 1],
+    ["PacketTooShort", 2],
+    ["NoMatch", 3],
+    ["StackOutOfBounds", 4],
+    ["HeaderTooShort", 5],
+    ["ParserTimeout", 6]
   ],
   "enums" : [],
   "parsers" : [
@@ -371,16 +364,6 @@
           ],
           "transitions" : [
             {
-              "value" : "0x9100",
-              "mask" : null,
-              "next_state" : "parse_vlan_tag"
-            },
-            {
-              "value" : "0x88a8",
-              "mask" : null,
-              "next_state" : "parse_vlan_tag"
-            },
-            {
               "value" : "0x8100",
               "mask" : null,
               "next_state" : "parse_vlan_tag"
@@ -401,11 +384,6 @@
               "next_state" : "parse_ipv4"
             },
             {
-              "value" : "0x86dd",
-              "mask" : null,
-              "next_state" : "parse_ipv6"
-            },
-            {
               "value" : "default",
               "mask" : null,
               "next_state" : null
@@ -434,11 +412,6 @@
           ],
           "transitions" : [
             {
-              "value" : "0x8100",
-              "mask" : null,
-              "next_state" : "parse_inner_vlan_tag"
-            },
-            {
               "value" : "0x0806",
               "mask" : null,
               "next_state" : "parse_arp"
@@ -449,52 +422,9 @@
               "next_state" : "parse_ipv4"
             },
             {
-              "value" : "0x86dd",
+              "value" : "0x8847",
               "mask" : null,
-              "next_state" : "parse_ipv6"
-            },
-            {
-              "value" : "default",
-              "mask" : null,
-              "next_state" : null
-            }
-          ],
-          "transition_key" : [
-            {
-              "type" : "field",
-              "value" : ["vlan_tag", "ether_type"]
-            }
-          ]
-        },
-        {
-          "name" : "parse_inner_vlan_tag",
-          "id" : 4,
-          "parser_ops" : [
-            {
-              "parameters" : [
-                {
-                  "type" : "regular",
-                  "value" : "inner_vlan_tag"
-                }
-              ],
-              "op" : "extract"
-            }
-          ],
-          "transitions" : [
-            {
-              "value" : "0x0806",
-              "mask" : null,
-              "next_state" : "parse_arp"
-            },
-            {
-              "value" : "0x0800",
-              "mask" : null,
-              "next_state" : "parse_ipv4"
-            },
-            {
-              "value" : "0x86dd",
-              "mask" : null,
-              "next_state" : "parse_ipv6"
+              "next_state" : "parse_mpls"
             },
             {
               "value" : "default",
@@ -511,7 +441,7 @@
         },
         {
           "name" : "parse_mpls",
-          "id" : 5,
+          "id" : 4,
           "parser_ops" : [
             {
               "parameters" : [
@@ -543,11 +473,6 @@
               "next_state" : "parse_ipv4"
             },
             {
-              "value" : "0x06",
-              "mask" : null,
-              "next_state" : "parse_ipv6"
-            },
-            {
               "value" : "default",
               "mask" : null,
               "next_state" : "parse_ethernet"
@@ -562,7 +487,7 @@
         },
         {
           "name" : "parse_ipv4",
-          "id" : 6,
+          "id" : 5,
           "parser_ops" : [
             {
               "parameters" : [
@@ -617,64 +542,8 @@
           ]
         },
         {
-          "name" : "parse_ipv6",
-          "id" : 7,
-          "parser_ops" : [
-            {
-              "parameters" : [
-                {
-                  "type" : "regular",
-                  "value" : "ipv6"
-                }
-              ],
-              "op" : "extract"
-            },
-            {
-              "parameters" : [
-                {
-                  "type" : "field",
-                  "value" : ["scalars", "fabric_metadata_t.ip_proto"]
-                },
-                {
-                  "type" : "field",
-                  "value" : ["ipv6", "next_hdr"]
-                }
-              ],
-              "op" : "set"
-            }
-          ],
-          "transitions" : [
-            {
-              "value" : "0x06",
-              "mask" : null,
-              "next_state" : "parse_tcp"
-            },
-            {
-              "value" : "0x11",
-              "mask" : null,
-              "next_state" : "parse_udp"
-            },
-            {
-              "value" : "0x3a",
-              "mask" : null,
-              "next_state" : "parse_icmp"
-            },
-            {
-              "value" : "default",
-              "mask" : null,
-              "next_state" : null
-            }
-          ],
-          "transition_key" : [
-            {
-              "type" : "field",
-              "value" : ["ipv6", "next_hdr"]
-            }
-          ]
-        },
-        {
           "name" : "parse_arp",
-          "id" : 8,
+          "id" : 6,
           "parser_ops" : [
             {
               "parameters" : [
@@ -697,7 +566,7 @@
         },
         {
           "name" : "parse_tcp",
-          "id" : 9,
+          "id" : 7,
           "parser_ops" : [
             {
               "parameters" : [
@@ -746,7 +615,7 @@
         },
         {
           "name" : "parse_udp",
-          "id" : 10,
+          "id" : 8,
           "parser_ops" : [
             {
               "parameters" : [
@@ -795,7 +664,7 @@
         },
         {
           "name" : "parse_icmp",
-          "id" : 11,
+          "id" : 9,
           "parser_ops" : [
             {
               "parameters" : [
@@ -824,21 +693,81 @@
       "name" : "deparser",
       "id" : 0,
       "source_info" : {
-        "filename" : "./include/parser.p4",
-        "line" : 137,
+        "filename" : "include/parser.p4",
+        "line" : 166,
         "column" : 8,
         "source_fragment" : "FabricDeparser"
       },
-      "order" : ["packet_in", "ethernet", "vlan_tag", "inner_vlan_tag", "mpls", "arp", "ipv4", "ipv6", "tcp", "udp", "icmp"]
+      "order" : ["packet_in", "ethernet", "vlan_tag", "mpls", "arp", "ipv4", "tcp", "udp", "icmp"]
     }
   ],
   "meter_arrays" : [],
   "counter_arrays" : [
     {
-      "name" : "port_counters_control.egress_port_counter",
+      "name" : "filtering.ingress_port_vlan_counter",
       "id" : 0,
+      "is_direct" : true,
+      "binding" : "filtering.ingress_port_vlan"
+    },
+    {
+      "name" : "filtering.fwd_classifier_counter",
+      "id" : 1,
+      "is_direct" : true,
+      "binding" : "filtering.fwd_classifier"
+    },
+    {
+      "name" : "forwarding.bridging_counter",
+      "id" : 2,
+      "is_direct" : true,
+      "binding" : "forwarding.bridging"
+    },
+    {
+      "name" : "forwarding.mpls_counter",
+      "id" : 3,
+      "is_direct" : true,
+      "binding" : "forwarding.mpls"
+    },
+    {
+      "name" : "forwarding.unicast_v4_counter",
+      "id" : 4,
+      "is_direct" : true,
+      "binding" : "forwarding.unicast_v4"
+    },
+    {
+      "name" : "forwarding.multicast_v4_counter",
+      "id" : 5,
+      "is_direct" : true,
+      "binding" : "forwarding.multicast_v4"
+    },
+    {
+      "name" : "forwarding.acl_counter",
+      "id" : 6,
+      "is_direct" : true,
+      "binding" : "forwarding.acl"
+    },
+    {
+      "name" : "next.simple_counter",
+      "id" : 7,
+      "is_direct" : true,
+      "binding" : "next.simple"
+    },
+    {
+      "name" : "next.hashed_counter",
+      "id" : 8,
+      "is_direct" : true,
+      "binding" : "next.hashed"
+    },
+    {
+      "name" : "next.broadcast_counter",
+      "id" : 9,
+      "is_direct" : true,
+      "binding" : "next.broadcast"
+    },
+    {
+      "name" : "port_counters_control.egress_port_counter",
+      "id" : 10,
       "source_info" : {
-        "filename" : "./include/control/port_counter.p4",
+        "filename" : "include/control/port_counter.p4",
         "line" : 23,
         "column" : 38,
         "source_fragment" : "egress_port_counter"
@@ -848,9 +777,9 @@
     },
     {
       "name" : "port_counters_control.ingress_port_counter",
-      "id" : 1,
+      "id" : 11,
       "source_info" : {
-        "filename" : "./include/control/port_counter.p4",
+        "filename" : "include/control/port_counter.p4",
         "line" : 24,
         "column" : 38,
         "source_fragment" : "ingress_port_counter"
@@ -865,8 +794,8 @@
       "name" : "calc",
       "id" : 0,
       "source_info" : {
-        "filename" : "./include/checksum.p4",
-        "line" : 51,
+        "filename" : "include/checksum.p4",
+        "line" : 56,
         "column" : 8,
         "source_fragment" : "verify_checksum(hdr.ipv4.isValid(), ..."
       },
@@ -922,8 +851,8 @@
       "name" : "calc_0",
       "id" : 1,
       "source_info" : {
-        "filename" : "./include/checksum.p4",
-        "line" : 25,
+        "filename" : "include/checksum.p4",
+        "line" : 28,
         "column" : 8,
         "source_fragment" : "update_checksum(hdr.ipv4.isValid(), ..."
       },
@@ -1033,28 +962,16 @@
       "primitives" : []
     },
     {
-      "name" : "NoAction",
-      "id" : 9,
-      "runtime_data" : [],
-      "primitives" : []
-    },
-    {
-      "name" : "NoAction",
-      "id" : 10,
-      "runtime_data" : [],
-      "primitives" : []
-    },
-    {
       "name" : "filtering.drop",
-      "id" : 11,
+      "id" : 9,
       "runtime_data" : [],
       "primitives" : [
         {
           "op" : "drop",
           "parameters" : [],
           "source_info" : {
-            "filename" : "./include/control/filtering.p4",
-            "line" : 29,
+            "filename" : "include/control/filtering.p4",
+            "line" : 31,
             "column" : 8,
             "source_fragment" : "mark_to_drop()"
           }
@@ -1063,7 +980,7 @@
     },
     {
       "name" : "filtering.set_vlan",
-      "id" : 12,
+      "id" : 10,
       "runtime_data" : [
         {
           "name" : "new_vlan_id",
@@ -1084,8 +1001,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/filtering.p4",
-            "line" : 33,
+            "filename" : "include/control/filtering.p4",
+            "line" : 35,
             "column" : 8,
             "source_fragment" : "hdr.vlan_tag.vlan_id = new_vlan_id"
           }
@@ -1094,7 +1011,7 @@
     },
     {
       "name" : "filtering.push_internal_vlan",
-      "id" : 13,
+      "id" : 11,
       "runtime_data" : [
         {
           "name" : "new_vlan_id",
@@ -1111,8 +1028,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/filtering.p4",
-            "line" : 39,
+            "filename" : "include/control/filtering.p4",
+            "line" : 41,
             "column" : 8,
             "source_fragment" : "hdr.vlan_tag.setValid()"
           }
@@ -1130,8 +1047,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/filtering.p4",
-            "line" : 40,
+            "filename" : "include/control/filtering.p4",
+            "line" : 42,
             "column" : 8,
             "source_fragment" : "hdr.vlan_tag.cfi = 0"
           }
@@ -1149,8 +1066,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/filtering.p4",
-            "line" : 41,
+            "filename" : "include/control/filtering.p4",
+            "line" : 43,
             "column" : 8,
             "source_fragment" : "hdr.vlan_tag.pri = 0"
           }
@@ -1168,8 +1085,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/filtering.p4",
-            "line" : 42,
+            "filename" : "include/control/filtering.p4",
+            "line" : 44,
             "column" : 8,
             "source_fragment" : "hdr.vlan_tag.ether_type = hdr.ethernet.ether_type"
           }
@@ -1187,7 +1104,7 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/../define.p4",
+            "filename" : "include/control/../define.p4",
             "line" : 32,
             "column" : 31,
             "source_fragment" : "0x8100; ..."
@@ -1206,8 +1123,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/filtering.p4",
-            "line" : 33,
+            "filename" : "include/control/filtering.p4",
+            "line" : 35,
             "column" : 8,
             "source_fragment" : "hdr.vlan_tag.vlan_id = new_vlan_id; ..."
           }
@@ -1235,8 +1152,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/filtering.p4",
-            "line" : 47,
+            "filename" : "include/control/filtering.p4",
+            "line" : 49,
             "column" : 8,
             "source_fragment" : "fabric_metadata.pop_vlan_at_egress = true"
           }
@@ -1245,7 +1162,7 @@
     },
     {
       "name" : "filtering.set_forwarding_type",
-      "id" : 14,
+      "id" : 12,
       "runtime_data" : [
         {
           "name" : "fwd_type",
@@ -1266,8 +1183,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/filtering.p4",
-            "line" : 51,
+            "filename" : "include/control/filtering.p4",
+            "line" : 53,
             "column" : 8,
             "source_fragment" : "fabric_metadata.fwd_type = fwd_type"
           }
@@ -1276,15 +1193,15 @@
     },
     {
       "name" : "forwarding.drop",
-      "id" : 15,
+      "id" : 13,
       "runtime_data" : [],
       "primitives" : [
         {
           "op" : "drop",
           "parameters" : [],
           "source_info" : {
-            "filename" : "./include/control/forwarding.p4",
-            "line" : 31,
+            "filename" : "include/control/forwarding.p4",
+            "line" : 37,
             "column" : 8,
             "source_fragment" : "mark_to_drop()"
           }
@@ -1293,6 +1210,68 @@
     },
     {
       "name" : "forwarding.set_next_id",
+      "id" : 14,
+      "runtime_data" : [
+        {
+          "name" : "next_id",
+          "bitwidth" : 32
+        }
+      ],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["scalars", "fabric_metadata_t.next_id"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 0
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/forwarding.p4",
+            "line" : 41,
+            "column" : 8,
+            "source_fragment" : "fabric_metadata.next_id = next_id"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "forwarding.set_next_id",
+      "id" : 15,
+      "runtime_data" : [
+        {
+          "name" : "next_id",
+          "bitwidth" : 32
+        }
+      ],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["scalars", "fabric_metadata_t.next_id"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 0
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/forwarding.p4",
+            "line" : 41,
+            "column" : 8,
+            "source_fragment" : "fabric_metadata.next_id = next_id"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "forwarding.set_next_id",
       "id" : 16,
       "runtime_data" : [
         {
@@ -1314,8 +1293,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/forwarding.p4",
-            "line" : 35,
+            "filename" : "include/control/forwarding.p4",
+            "line" : 41,
             "column" : 8,
             "source_fragment" : "fabric_metadata.next_id = next_id"
           }
@@ -1345,132 +1324,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/forwarding.p4",
-            "line" : 35,
-            "column" : 8,
-            "source_fragment" : "fabric_metadata.next_id = next_id"
-          }
-        }
-      ]
-    },
-    {
-      "name" : "forwarding.set_next_id",
-      "id" : 18,
-      "runtime_data" : [
-        {
-          "name" : "next_id",
-          "bitwidth" : 32
-        }
-      ],
-      "primitives" : [
-        {
-          "op" : "assign",
-          "parameters" : [
-            {
-              "type" : "field",
-              "value" : ["scalars", "fabric_metadata_t.next_id"]
-            },
-            {
-              "type" : "runtime_data",
-              "value" : 0
-            }
-          ],
-          "source_info" : {
-            "filename" : "./include/control/forwarding.p4",
-            "line" : 35,
-            "column" : 8,
-            "source_fragment" : "fabric_metadata.next_id = next_id"
-          }
-        }
-      ]
-    },
-    {
-      "name" : "forwarding.set_next_id",
-      "id" : 19,
-      "runtime_data" : [
-        {
-          "name" : "next_id",
-          "bitwidth" : 32
-        }
-      ],
-      "primitives" : [
-        {
-          "op" : "assign",
-          "parameters" : [
-            {
-              "type" : "field",
-              "value" : ["scalars", "fabric_metadata_t.next_id"]
-            },
-            {
-              "type" : "runtime_data",
-              "value" : 0
-            }
-          ],
-          "source_info" : {
-            "filename" : "./include/control/forwarding.p4",
-            "line" : 35,
-            "column" : 8,
-            "source_fragment" : "fabric_metadata.next_id = next_id"
-          }
-        }
-      ]
-    },
-    {
-      "name" : "forwarding.set_next_id",
-      "id" : 20,
-      "runtime_data" : [
-        {
-          "name" : "next_id",
-          "bitwidth" : 32
-        }
-      ],
-      "primitives" : [
-        {
-          "op" : "assign",
-          "parameters" : [
-            {
-              "type" : "field",
-              "value" : ["scalars", "fabric_metadata_t.next_id"]
-            },
-            {
-              "type" : "runtime_data",
-              "value" : 0
-            }
-          ],
-          "source_info" : {
-            "filename" : "./include/control/forwarding.p4",
-            "line" : 35,
-            "column" : 8,
-            "source_fragment" : "fabric_metadata.next_id = next_id"
-          }
-        }
-      ]
-    },
-    {
-      "name" : "forwarding.set_next_id",
-      "id" : 21,
-      "runtime_data" : [
-        {
-          "name" : "next_id",
-          "bitwidth" : 32
-        }
-      ],
-      "primitives" : [
-        {
-          "op" : "assign",
-          "parameters" : [
-            {
-              "type" : "field",
-              "value" : ["scalars", "fabric_metadata_t.next_id"]
-            },
-            {
-              "type" : "runtime_data",
-              "value" : 0
-            }
-          ],
-          "source_info" : {
-            "filename" : "./include/control/forwarding.p4",
-            "line" : 35,
+            "filename" : "include/control/forwarding.p4",
+            "line" : 41,
             "column" : 8,
             "source_fragment" : "fabric_metadata.next_id = next_id"
           }
@@ -1479,7 +1334,7 @@
     },
     {
       "name" : "forwarding.pop_mpls_and_next",
-      "id" : 22,
+      "id" : 18,
       "runtime_data" : [
         {
           "name" : "next_id",
@@ -1496,8 +1351,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/forwarding.p4",
-            "line" : 39,
+            "filename" : "include/control/forwarding.p4",
+            "line" : 45,
             "column" : 8,
             "source_fragment" : "hdr.mpls.setInvalid()"
           }
@@ -1515,8 +1370,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/forwarding.p4",
-            "line" : 40,
+            "filename" : "include/control/forwarding.p4",
+            "line" : 46,
             "column" : 8,
             "source_fragment" : "fabric_metadata.next_id = next_id"
           }
@@ -1525,7 +1380,7 @@
     },
     {
       "name" : "forwarding.duplicate_to_controller",
-      "id" : 23,
+      "id" : 19,
       "runtime_data" : [],
       "primitives" : [
         {
@@ -1541,8 +1396,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/../define.p4",
-            "line" : 48,
+            "filename" : "include/control/../define.p4",
+            "line" : 50,
             "column" : 28,
             "source_fragment" : "255; ..."
           }
@@ -1551,7 +1406,7 @@
     },
     {
       "name" : "next.output",
-      "id" : 24,
+      "id" : 20,
       "runtime_data" : [
         {
           "name" : "port_num",
@@ -1572,8 +1427,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 30,
+            "filename" : "include/control/next.p4",
+            "line" : 33,
             "column" : 8,
             "source_fragment" : "standard_metadata.egress_spec = port_num"
           }
@@ -1582,7 +1437,7 @@
     },
     {
       "name" : "next.set_vlan_output",
-      "id" : 25,
+      "id" : 21,
       "runtime_data" : [
         {
           "name" : "new_vlan_id",
@@ -1607,8 +1462,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 34,
+            "filename" : "include/control/next.p4",
+            "line" : 37,
             "column" : 8,
             "source_fragment" : "hdr.vlan_tag.vlan_id = new_vlan_id"
           }
@@ -1636,8 +1491,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 37,
+            "filename" : "include/control/next.p4",
+            "line" : 40,
             "column" : 8,
             "source_fragment" : "fabric_metadata.pop_vlan_at_egress = false"
           }
@@ -1655,8 +1510,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 30,
+            "filename" : "include/control/next.p4",
+            "line" : 33,
             "column" : 8,
             "source_fragment" : "standard_metadata.egress_spec = port_num; ..."
           }
@@ -1665,7 +1520,7 @@
     },
     {
       "name" : "next.l3_routing",
-      "id" : 26,
+      "id" : 22,
       "runtime_data" : [
         {
           "name" : "port_num",
@@ -1694,8 +1549,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 42,
+            "filename" : "include/control/next.p4",
+            "line" : 45,
             "column" : 8,
             "source_fragment" : "hdr.ethernet.src_addr = smac; ..."
           }
@@ -1713,8 +1568,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 46,
+            "filename" : "include/control/next.p4",
+            "line" : 49,
             "column" : 8,
             "source_fragment" : "hdr.ethernet.dst_addr = dmac; ..."
           }
@@ -1732,8 +1587,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 30,
+            "filename" : "include/control/next.p4",
+            "line" : 33,
             "column" : 8,
             "source_fragment" : "standard_metadata.egress_spec = port_num; ..."
           }
@@ -1742,7 +1597,7 @@
     },
     {
       "name" : "next.l3_routing",
-      "id" : 27,
+      "id" : 23,
       "runtime_data" : [
         {
           "name" : "port_num",
@@ -1771,8 +1626,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 42,
+            "filename" : "include/control/next.p4",
+            "line" : 45,
             "column" : 8,
             "source_fragment" : "hdr.ethernet.src_addr = smac; ..."
           }
@@ -1790,8 +1645,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 46,
+            "filename" : "include/control/next.p4",
+            "line" : 49,
             "column" : 8,
             "source_fragment" : "hdr.ethernet.dst_addr = dmac; ..."
           }
@@ -1809,8 +1664,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 30,
+            "filename" : "include/control/next.p4",
+            "line" : 33,
             "column" : 8,
             "source_fragment" : "standard_metadata.egress_spec = port_num; ..."
           }
@@ -1819,7 +1674,7 @@
     },
     {
       "name" : "next.set_mcast_group",
-      "id" : 28,
+      "id" : 24,
       "runtime_data" : [
         {
           "name" : "gid",
@@ -1844,8 +1699,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 56,
+            "filename" : "include/control/next.p4",
+            "line" : 59,
             "column" : 8,
             "source_fragment" : "standard_metadata.mcast_grp = gid"
           }
@@ -1863,8 +1718,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 42,
+            "filename" : "include/control/next.p4",
+            "line" : 45,
             "column" : 8,
             "source_fragment" : "hdr.ethernet.src_addr = smac; ..."
           }
@@ -1873,7 +1728,7 @@
     },
     {
       "name" : "next.mpls_routing_v4",
-      "id" : 29,
+      "id" : 25,
       "runtime_data" : [
         {
           "name" : "port_num",
@@ -1906,8 +1761,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 42,
+            "filename" : "include/control/next.p4",
+            "line" : 45,
             "column" : 8,
             "source_fragment" : "hdr.ethernet.src_addr = smac; ..."
           }
@@ -1925,8 +1780,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 46,
+            "filename" : "include/control/next.p4",
+            "line" : 49,
             "column" : 8,
             "source_fragment" : "hdr.ethernet.dst_addr = dmac; ..."
           }
@@ -1944,8 +1799,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 30,
+            "filename" : "include/control/next.p4",
+            "line" : 33,
             "column" : 8,
             "source_fragment" : "standard_metadata.egress_spec = port_num; ..."
           }
@@ -1959,8 +1814,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 62,
+            "filename" : "include/control/next.p4",
+            "line" : 65,
             "column" : 8,
             "source_fragment" : "hdr.mpls.setValid()"
           }
@@ -1970,7 +1825,7 @@
           "parameters" : [
             {
               "type" : "field",
-              "value" : ["ethernet", "ether_type"]
+              "value" : ["vlan_tag", "ether_type"]
             },
             {
               "type" : "hexstr",
@@ -1978,7 +1833,7 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/../define.p4",
+            "filename" : "include/control/../define.p4",
             "line" : 33,
             "column" : 31,
             "source_fragment" : "0x8847; ..."
@@ -1997,8 +1852,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 64,
+            "filename" : "include/control/next.p4",
+            "line" : 67,
             "column" : 8,
             "source_fragment" : "hdr.mpls.label = label; ..."
           }
@@ -2016,8 +1871,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 65,
+            "filename" : "include/control/next.p4",
+            "line" : 68,
             "column" : 8,
             "source_fragment" : "hdr.mpls.tc = tc; ..."
           }
@@ -2035,8 +1890,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 66,
+            "filename" : "include/control/next.p4",
+            "line" : 69,
             "column" : 8,
             "source_fragment" : "hdr.mpls.bos = 1w1"
           }
@@ -2054,8 +1909,199 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/../header.p4",
-            "line" : 19,
+            "filename" : "include/control/../define.p4",
+            "line" : 60,
+            "column" : 32,
+            "source_fragment" : "64; ..."
+          }
+        }
+      ]
+    },
+    {
+      "name" : "next.mpls_routing_v4",
+      "id" : 26,
+      "runtime_data" : [
+        {
+          "name" : "port_num",
+          "bitwidth" : 9
+        },
+        {
+          "name" : "smac",
+          "bitwidth" : 48
+        },
+        {
+          "name" : "dmac",
+          "bitwidth" : 48
+        },
+        {
+          "name" : "label",
+          "bitwidth" : 20
+        }
+      ],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["ethernet", "src_addr"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 1
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 45,
+            "column" : 8,
+            "source_fragment" : "hdr.ethernet.src_addr = smac; ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["ethernet", "dst_addr"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 2
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 49,
+            "column" : 8,
+            "source_fragment" : "hdr.ethernet.dst_addr = dmac; ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["standard_metadata", "egress_spec"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 0
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 33,
+            "column" : 8,
+            "source_fragment" : "standard_metadata.egress_spec = port_num; ..."
+          }
+        },
+        {
+          "op" : "add_header",
+          "parameters" : [
+            {
+              "type" : "header",
+              "value" : "mpls"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 65,
+            "column" : 8,
+            "source_fragment" : "hdr.mpls.setValid()"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["vlan_tag", "ether_type"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x8847"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/../define.p4",
+            "line" : 33,
+            "column" : 31,
+            "source_fragment" : "0x8847; ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["mpls", "label"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 3
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 67,
+            "column" : 8,
+            "source_fragment" : "hdr.mpls.label = label; ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["mpls", "tc"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x00"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 68,
+            "column" : 8,
+            "source_fragment" : "hdr.mpls.tc = tc; ..."
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["mpls", "bos"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x01"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 69,
+            "column" : 8,
+            "source_fragment" : "hdr.mpls.bos = 1w1"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["mpls", "ttl"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x40"
+            }
+          ],
+          "source_info" : {
+            "filename" : "include/control/../define.p4",
+            "line" : 60,
             "column" : 32,
             "source_fragment" : "64; ..."
           }
@@ -2064,7 +2110,7 @@
     },
     {
       "name" : "next.mpls_routing_v6",
-      "id" : 30,
+      "id" : 27,
       "runtime_data" : [
         {
           "name" : "port_num",
@@ -2097,8 +2143,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 42,
+            "filename" : "include/control/next.p4",
+            "line" : 45,
             "column" : 8,
             "source_fragment" : "hdr.ethernet.src_addr = smac; ..."
           }
@@ -2116,8 +2162,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 46,
+            "filename" : "include/control/next.p4",
+            "line" : 49,
             "column" : 8,
             "source_fragment" : "hdr.ethernet.dst_addr = dmac; ..."
           }
@@ -2135,8 +2181,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 30,
+            "filename" : "include/control/next.p4",
+            "line" : 33,
             "column" : 8,
             "source_fragment" : "standard_metadata.egress_spec = port_num; ..."
           }
@@ -2150,8 +2196,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 62,
+            "filename" : "include/control/next.p4",
+            "line" : 65,
             "column" : 8,
             "source_fragment" : "hdr.mpls.setValid()"
           }
@@ -2161,7 +2207,7 @@
           "parameters" : [
             {
               "type" : "field",
-              "value" : ["ethernet", "ether_type"]
+              "value" : ["vlan_tag", "ether_type"]
             },
             {
               "type" : "hexstr",
@@ -2169,7 +2215,7 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/../define.p4",
+            "filename" : "include/control/../define.p4",
             "line" : 33,
             "column" : 31,
             "source_fragment" : "0x8847; ..."
@@ -2188,8 +2234,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 64,
+            "filename" : "include/control/next.p4",
+            "line" : 67,
             "column" : 8,
             "source_fragment" : "hdr.mpls.label = label; ..."
           }
@@ -2207,8 +2253,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 65,
+            "filename" : "include/control/next.p4",
+            "line" : 68,
             "column" : 8,
             "source_fragment" : "hdr.mpls.tc = tc; ..."
           }
@@ -2226,8 +2272,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 66,
+            "filename" : "include/control/next.p4",
+            "line" : 69,
             "column" : 8,
             "source_fragment" : "hdr.mpls.bos = 1w1"
           }
@@ -2245,8 +2291,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/../header.p4",
-            "line" : 19,
+            "filename" : "include/control/../define.p4",
+            "line" : 60,
             "column" : 32,
             "source_fragment" : "64; ..."
           }
@@ -2255,7 +2301,7 @@
     },
     {
       "name" : "act",
-      "id" : 31,
+      "id" : 28,
       "runtime_data" : [],
       "primitives" : [
         {
@@ -2271,7 +2317,7 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/packetio.p4",
+            "filename" : "include/control/packetio.p4",
             "line" : 26,
             "column" : 12,
             "source_fragment" : "standard_metadata.egress_spec = hdr.packet_out.egress_port"
@@ -2286,7 +2332,7 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/packetio.p4",
+            "filename" : "include/control/packetio.p4",
             "line" : 27,
             "column" : 12,
             "source_fragment" : "hdr.packet_out.setInvalid()"
@@ -2296,7 +2342,7 @@
     },
     {
       "name" : "act_0",
-      "id" : 32,
+      "id" : 29,
       "runtime_data" : [],
       "primitives" : [
         {
@@ -2304,7 +2350,7 @@
           "parameters" : [
             {
               "type" : "field",
-              "value" : ["ethernet", "ether_type"]
+              "value" : ["vlan_tag", "ether_type"]
             },
             {
               "type" : "hexstr",
@@ -2312,7 +2358,7 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/../define.p4",
+            "filename" : "include/control/../define.p4",
             "line" : 35,
             "column" : 31,
             "source_fragment" : "0x0800; ..."
@@ -2331,7 +2377,7 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/../define.p4",
+            "filename" : "include/control/../define.p4",
             "line" : 35,
             "column" : 31,
             "source_fragment" : "0x0800; ..."
@@ -2341,52 +2387,7 @@
     },
     {
       "name" : "act_1",
-      "id" : 33,
-      "runtime_data" : [],
-      "primitives" : [
-        {
-          "op" : "assign",
-          "parameters" : [
-            {
-              "type" : "field",
-              "value" : ["ethernet", "ether_type"]
-            },
-            {
-              "type" : "hexstr",
-              "value" : "0x86dd"
-            }
-          ],
-          "source_info" : {
-            "filename" : "./include/control/../define.p4",
-            "line" : 36,
-            "column" : 31,
-            "source_fragment" : "0x86dd; ..."
-          }
-        },
-        {
-          "op" : "assign",
-          "parameters" : [
-            {
-              "type" : "field",
-              "value" : ["scalars", "fabric_metadata_t.original_ether_type"]
-            },
-            {
-              "type" : "hexstr",
-              "value" : "0x86dd"
-            }
-          ],
-          "source_info" : {
-            "filename" : "./include/control/../define.p4",
-            "line" : 36,
-            "column" : 31,
-            "source_fragment" : "0x86dd; ..."
-          }
-        }
-      ]
-    },
-    {
-      "name" : "act_2",
-      "id" : 34,
+      "id" : 30,
       "runtime_data" : [],
       "primitives" : [
         {
@@ -2415,8 +2416,8 @@
       ]
     },
     {
-      "name" : "act_3",
-      "id" : 35,
+      "name" : "act_2",
+      "id" : 31,
       "runtime_data" : [],
       "primitives" : [
         {
@@ -2445,8 +2446,8 @@
       ]
     },
     {
-      "name" : "act_4",
-      "id" : 36,
+      "name" : "act_3",
+      "id" : 32,
       "runtime_data" : [],
       "primitives" : [
         {
@@ -2485,8 +2486,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 133,
+            "filename" : "include/control/next.p4",
+            "line" : 140,
             "column" : 20,
             "source_fragment" : "hdr.ipv4.ttl = hdr.ipv4.ttl - 1"
           }
@@ -2494,57 +2495,8 @@
       ]
     },
     {
-      "name" : "act_5",
-      "id" : 37,
-      "runtime_data" : [],
-      "primitives" : [
-        {
-          "op" : "assign",
-          "parameters" : [
-            {
-              "type" : "field",
-              "value" : ["ipv6", "hop_limit"]
-            },
-            {
-              "type" : "expression",
-              "value" : {
-                "type" : "expression",
-                "value" : {
-                  "op" : "&",
-                  "left" : {
-                    "type" : "expression",
-                    "value" : {
-                      "op" : "+",
-                      "left" : {
-                        "type" : "field",
-                        "value" : ["ipv6", "hop_limit"]
-                      },
-                      "right" : {
-                        "type" : "hexstr",
-                        "value" : "0xff"
-                      }
-                    }
-                  },
-                  "right" : {
-                    "type" : "hexstr",
-                    "value" : "0xff"
-                  }
-                }
-              }
-            }
-          ],
-          "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 136,
-            "column" : 20,
-            "source_fragment" : "hdr.ipv6.hop_limit = hdr.ipv6.hop_limit - 1"
-          }
-        }
-      ]
-    },
-    {
-      "name" : "act_6",
-      "id" : 38,
+      "name" : "act_4",
+      "id" : 33,
       "runtime_data" : [],
       "primitives" : [
         {
@@ -2586,7 +2538,7 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/port_counter.p4",
+            "filename" : "include/control/port_counter.p4",
             "line" : 28,
             "column" : 12,
             "source_fragment" : "egress_port_counter.count((bit<32>)standard_metadata.egress_spec)"
@@ -2595,8 +2547,8 @@
       ]
     },
     {
-      "name" : "act_7",
-      "id" : 39,
+      "name" : "act_5",
+      "id" : 34,
       "runtime_data" : [],
       "primitives" : [
         {
@@ -2638,7 +2590,7 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/port_counter.p4",
+            "filename" : "include/control/port_counter.p4",
             "line" : 31,
             "column" : 12,
             "source_fragment" : "ingress_port_counter.count((bit<32>)standard_metadata.ingress_port)"
@@ -2647,34 +2599,8 @@
       ]
     },
     {
-      "name" : "act_8",
-      "id" : 40,
-      "runtime_data" : [],
-      "primitives" : [
-        {
-          "op" : "assign",
-          "parameters" : [
-            {
-              "type" : "field",
-              "value" : ["ethernet", "ether_type"]
-            },
-            {
-              "type" : "hexstr",
-              "value" : "0x8847"
-            }
-          ],
-          "source_info" : {
-            "filename" : "./include/control/../define.p4",
-            "line" : 33,
-            "column" : 31,
-            "source_fragment" : "0x8847; ..."
-          }
-        }
-      ]
-    },
-    {
-      "name" : "act_9",
-      "id" : 41,
+      "name" : "act_6",
+      "id" : 35,
       "runtime_data" : [],
       "primitives" : [
         {
@@ -2686,23 +2612,16 @@
             },
             {
               "type" : "field",
-              "value" : ["scalars", "fabric_metadata_t.original_ether_type"]
+              "value" : ["vlan_tag", "ether_type"]
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 156,
-            "column" : 16,
-            "source_fragment" : "hdr.ethernet.ether_type = fabric_metadata.original_ether_type"
+            "filename" : "include/control/next.p4",
+            "line" : 162,
+            "column" : 12,
+            "source_fragment" : "hdr.ethernet.ether_type = hdr.vlan_tag.ether_type"
           }
-        }
-      ]
-    },
-    {
-      "name" : "act_10",
-      "id" : 42,
-      "runtime_data" : [],
-      "primitives" : [
+        },
         {
           "op" : "remove_header",
           "parameters" : [
@@ -2712,8 +2631,8 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 158,
+            "filename" : "include/control/next.p4",
+            "line" : 163,
             "column" : 12,
             "source_fragment" : "hdr.vlan_tag.setInvalid()"
           }
@@ -2721,8 +2640,8 @@
       ]
     },
     {
-      "name" : "act_11",
-      "id" : 43,
+      "name" : "act_7",
+      "id" : 36,
       "runtime_data" : [],
       "primitives" : [
         {
@@ -2734,7 +2653,7 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/packetio.p4",
+            "filename" : "include/control/packetio.p4",
             "line" : 39,
             "column" : 12,
             "source_fragment" : "hdr.packet_in.setValid()"
@@ -2753,7 +2672,7 @@
             }
           ],
           "source_info" : {
-            "filename" : "./include/control/packetio.p4",
+            "filename" : "include/control/packetio.p4",
             "line" : 40,
             "column" : 12,
             "source_fragment" : "hdr.packet_in.ingress_port = standard_metadata.ingress_port"
@@ -2768,7 +2687,7 @@
       "id" : 0,
       "source_info" : {
         "filename" : "fabric.p4",
-        "line" : 29,
+        "line" : 33,
         "column" : 8,
         "source_fragment" : "FabricIngress"
       },
@@ -2784,14 +2703,14 @@
           "with_counters" : false,
           "support_timeout" : false,
           "direct_meters" : null,
-          "action_ids" : [31],
+          "action_ids" : [28],
           "actions" : ["act"],
           "base_default_next" : null,
           "next_tables" : {
             "act" : null
           },
           "default_entry" : {
-            "action_id" : 31,
+            "action_id" : 28,
             "action_const" : true,
             "action_data" : [],
             "action_entry_const" : true
@@ -2801,8 +2720,8 @@
           "name" : "filtering.ingress_port_vlan",
           "id" : 1,
           "source_info" : {
-            "filename" : "./include/control/filtering.p4",
-            "line" : 55,
+            "filename" : "include/control/filtering.p4",
+            "line" : 57,
             "column" : 10,
             "source_fragment" : "ingress_port_vlan"
           },
@@ -2826,10 +2745,10 @@
           "match_type" : "ternary",
           "type" : "simple",
           "max_size" : 1024,
-          "with_counters" : false,
+          "with_counters" : true,
           "support_timeout" : false,
           "direct_meters" : null,
-          "action_ids" : [13, 12, 0, 11],
+          "action_ids" : [11, 10, 0, 9],
           "actions" : ["filtering.push_internal_vlan", "filtering.set_vlan", "nop", "filtering.drop"],
           "base_default_next" : "filtering.fwd_classifier",
           "next_tables" : {
@@ -2839,7 +2758,7 @@
             "filtering.drop" : "filtering.fwd_classifier"
           },
           "default_entry" : {
-            "action_id" : 11,
+            "action_id" : 0,
             "action_const" : true,
             "action_data" : [],
             "action_entry_const" : true
@@ -2849,8 +2768,8 @@
           "name" : "filtering.fwd_classifier",
           "id" : 2,
           "source_info" : {
-            "filename" : "./include/control/filtering.p4",
-            "line" : 72,
+            "filename" : "include/control/filtering.p4",
+            "line" : 76,
             "column" : 10,
             "source_fragment" : "fwd_classifier"
           },
@@ -2874,17 +2793,17 @@
           "match_type" : "exact",
           "type" : "simple",
           "max_size" : 1024,
-          "with_counters" : false,
+          "with_counters" : true,
           "support_timeout" : false,
           "direct_meters" : null,
-          "action_ids" : [14],
+          "action_ids" : [12],
           "actions" : ["filtering.set_forwarding_type"],
           "base_default_next" : "node_6",
           "next_tables" : {
             "filtering.set_forwarding_type" : "node_6"
           },
           "default_entry" : {
-            "action_id" : 14,
+            "action_id" : 12,
             "action_const" : true,
             "action_data" : ["0x0"],
             "action_entry_const" : true
@@ -2894,8 +2813,8 @@
           "name" : "forwarding.bridging",
           "id" : 3,
           "source_info" : {
-            "filename" : "./include/control/forwarding.p4",
-            "line" : 47,
+            "filename" : "include/control/forwarding.p4",
+            "line" : 53,
             "column" : 10,
             "source_fragment" : "bridging"
           },
@@ -2914,10 +2833,10 @@
           "match_type" : "ternary",
           "type" : "simple",
           "max_size" : 1024,
-          "with_counters" : false,
+          "with_counters" : true,
           "support_timeout" : false,
           "direct_meters" : null,
-          "action_ids" : [16, 2],
+          "action_ids" : [14, 2],
           "actions" : ["forwarding.set_next_id", "NoAction"],
           "base_default_next" : "forwarding.acl",
           "next_tables" : {
@@ -2935,8 +2854,8 @@
           "name" : "forwarding.mpls",
           "id" : 4,
           "source_info" : {
-            "filename" : "./include/control/forwarding.p4",
-            "line" : 58,
+            "filename" : "include/control/forwarding.p4",
+            "line" : 65,
             "column" : 10,
             "source_fragment" : "mpls"
           },
@@ -2950,15 +2869,15 @@
           "match_type" : "exact",
           "type" : "simple",
           "max_size" : 1024,
-          "with_counters" : false,
+          "with_counters" : true,
           "support_timeout" : false,
           "direct_meters" : null,
-          "action_ids" : [22, 3],
+          "action_ids" : [18, 3],
           "actions" : ["forwarding.pop_mpls_and_next", "NoAction"],
-          "base_default_next" : "node_10",
+          "base_default_next" : "tbl_act_0",
           "next_tables" : {
-            "forwarding.pop_mpls_and_next" : "node_10",
-            "NoAction" : "node_10"
+            "forwarding.pop_mpls_and_next" : "tbl_act_0",
+            "NoAction" : "tbl_act_0"
           },
           "default_entry" : {
             "action_id" : 3,
@@ -2977,37 +2896,14 @@
           "with_counters" : false,
           "support_timeout" : false,
           "direct_meters" : null,
-          "action_ids" : [32],
+          "action_ids" : [29],
           "actions" : ["act_0"],
           "base_default_next" : "forwarding.acl",
           "next_tables" : {
             "act_0" : "forwarding.acl"
           },
           "default_entry" : {
-            "action_id" : 32,
-            "action_const" : true,
-            "action_data" : [],
-            "action_entry_const" : true
-          }
-        },
-        {
-          "name" : "tbl_act_1",
-          "id" : 6,
-          "key" : [],
-          "match_type" : "exact",
-          "type" : "simple",
-          "max_size" : 1024,
-          "with_counters" : false,
-          "support_timeout" : false,
-          "direct_meters" : null,
-          "action_ids" : [33],
-          "actions" : ["act_1"],
-          "base_default_next" : "forwarding.acl",
-          "next_tables" : {
-            "act_1" : "forwarding.acl"
-          },
-          "default_entry" : {
-            "action_id" : 33,
+            "action_id" : 29,
             "action_const" : true,
             "action_data" : [],
             "action_entry_const" : true
@@ -3015,10 +2911,10 @@
         },
         {
           "name" : "forwarding.unicast_v4",
-          "id" : 7,
+          "id" : 6,
           "source_info" : {
-            "filename" : "./include/control/forwarding.p4",
-            "line" : 68,
+            "filename" : "include/control/forwarding.p4",
+            "line" : 76,
             "column" : 10,
             "source_fragment" : "unicast_v4"
           },
@@ -3032,10 +2928,10 @@
           "match_type" : "lpm",
           "type" : "simple",
           "max_size" : 1024,
-          "with_counters" : false,
+          "with_counters" : true,
           "support_timeout" : false,
           "direct_meters" : null,
-          "action_ids" : [17, 4],
+          "action_ids" : [15, 4],
           "actions" : ["forwarding.set_next_id", "NoAction"],
           "base_default_next" : "forwarding.acl",
           "next_tables" : {
@@ -3051,10 +2947,10 @@
         },
         {
           "name" : "forwarding.multicast_v4",
-          "id" : 8,
+          "id" : 7,
           "source_info" : {
-            "filename" : "./include/control/forwarding.p4",
-            "line" : 78,
+            "filename" : "include/control/forwarding.p4",
+            "line" : 87,
             "column" : 10,
             "source_fragment" : "multicast_v4"
           },
@@ -3073,10 +2969,10 @@
           "match_type" : "lpm",
           "type" : "simple",
           "max_size" : 1024,
-          "with_counters" : false,
+          "with_counters" : true,
           "support_timeout" : false,
           "direct_meters" : null,
-          "action_ids" : [18, 5],
+          "action_ids" : [16, 5],
           "actions" : ["forwarding.set_next_id", "NoAction"],
           "base_default_next" : "forwarding.acl",
           "next_tables" : {
@@ -3091,88 +2987,11 @@
           }
         },
         {
-          "name" : "forwarding.unicast_v6",
-          "id" : 9,
-          "source_info" : {
-            "filename" : "./include/control/forwarding.p4",
-            "line" : 89,
-            "column" : 10,
-            "source_fragment" : "unicast_v6"
-          },
-          "key" : [
-            {
-              "match_type" : "lpm",
-              "target" : ["ipv6", "dst_addr"],
-              "mask" : null
-            }
-          ],
-          "match_type" : "lpm",
-          "type" : "simple",
-          "max_size" : 1024,
-          "with_counters" : false,
-          "support_timeout" : false,
-          "direct_meters" : null,
-          "action_ids" : [19, 6],
-          "actions" : ["forwarding.set_next_id", "NoAction"],
-          "base_default_next" : "forwarding.acl",
-          "next_tables" : {
-            "forwarding.set_next_id" : "forwarding.acl",
-            "NoAction" : "forwarding.acl"
-          },
-          "default_entry" : {
-            "action_id" : 6,
-            "action_const" : false,
-            "action_data" : [],
-            "action_entry_const" : false
-          }
-        },
-        {
-          "name" : "forwarding.multicast_v6",
-          "id" : 10,
-          "source_info" : {
-            "filename" : "./include/control/forwarding.p4",
-            "line" : 99,
-            "column" : 10,
-            "source_fragment" : "multicast_v6"
-          },
-          "key" : [
-            {
-              "match_type" : "exact",
-              "target" : ["vlan_tag", "vlan_id"],
-              "mask" : null
-            },
-            {
-              "match_type" : "lpm",
-              "target" : ["ipv6", "dst_addr"],
-              "mask" : null
-            }
-          ],
-          "match_type" : "lpm",
-          "type" : "simple",
-          "max_size" : 1024,
-          "with_counters" : false,
-          "support_timeout" : false,
-          "direct_meters" : null,
-          "action_ids" : [20, 7],
-          "actions" : ["forwarding.set_next_id", "NoAction"],
-          "base_default_next" : "forwarding.acl",
-          "next_tables" : {
-            "forwarding.set_next_id" : "forwarding.acl",
-            "NoAction" : "forwarding.acl"
-          },
-          "default_entry" : {
-            "action_id" : 7,
-            "action_const" : false,
-            "action_data" : [],
-            "action_entry_const" : false
-          }
-        },
-        {
           "name" : "forwarding.acl",
-          "id" : 11,
+          "id" : 8,
           "source_info" : {
-            "filename" : "./include/control/forwarding.p4",
-            "line" : 110,
+            "filename" : "include/control/forwarding.p4",
+            "line" : 127,
             "column" : 10,
             "source_fragment" : "acl"
           },
@@ -3241,10 +3060,10 @@
           "match_type" : "ternary",
           "type" : "simple",
           "max_size" : 256,
-          "with_counters" : false,
+          "with_counters" : true,
           "support_timeout" : false,
           "direct_meters" : null,
-          "action_ids" : [21, 23, 15, 1],
+          "action_ids" : [17, 19, 13, 1],
           "actions" : ["forwarding.set_next_id", "forwarding.duplicate_to_controller", "forwarding.drop", "nop"],
           "base_default_next" : "next.simple",
           "next_tables" : {
@@ -3262,10 +3081,10 @@
         },
         {
           "name" : "next.simple",
-          "id" : 12,
+          "id" : 9,
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 86,
+            "filename" : "include/control/next.p4",
+            "line" : 89,
             "column" : 10,
             "source_fragment" : "simple"
           },
@@ -3279,26 +3098,26 @@
           "match_type" : "exact",
           "type" : "simple",
           "max_size" : 1024,
-          "with_counters" : false,
+          "with_counters" : true,
           "support_timeout" : false,
           "direct_meters" : null,
-          "action_ids" : [24, 25, 26, 8],
-          "actions" : ["next.output", "next.set_vlan_output", "next.l3_routing", "NoAction"],
+          "action_ids" : [20, 21, 22, 25, 6],
+          "actions" : ["next.output", "next.set_vlan_output", "next.l3_routing", "next.mpls_routing_v4", "NoAction"],
           "base_default_next" : null,
           "next_tables" : {
-            "__HIT__" : "tbl_act_2",
-            "__MISS__" : "tbl_act_3"
+            "__HIT__" : "tbl_act_1",
+            "__MISS__" : "tbl_act_2"
           },
           "default_entry" : {
-            "action_id" : 8,
+            "action_id" : 6,
             "action_const" : false,
             "action_data" : [],
             "action_entry_const" : false
           }
         },
         {
-          "name" : "tbl_act_2",
-          "id" : 13,
+          "name" : "tbl_act_1",
+          "id" : 10,
           "key" : [],
           "match_type" : "exact",
           "type" : "simple",
@@ -3306,14 +3125,37 @@
           "with_counters" : false,
           "support_timeout" : false,
           "direct_meters" : null,
-          "action_ids" : [34],
-          "actions" : ["act_2"],
-          "base_default_next" : "node_25",
+          "action_ids" : [30],
+          "actions" : ["act_1"],
+          "base_default_next" : "node_19",
           "next_tables" : {
-            "act_2" : "node_25"
+            "act_1" : "node_19"
           },
           "default_entry" : {
-            "action_id" : 34,
+            "action_id" : 30,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        },
+        {
+          "name" : "tbl_act_2",
+          "id" : 11,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [31],
+          "actions" : ["act_2"],
+          "base_default_next" : "node_19",
+          "next_tables" : {
+            "act_2" : "node_19"
+          },
+          "default_entry" : {
+            "action_id" : 31,
             "action_const" : true,
             "action_data" : [],
             "action_entry_const" : true
@@ -3321,7 +3163,7 @@
         },
         {
           "name" : "tbl_act_3",
-          "id" : 14,
+          "id" : 12,
           "key" : [],
           "match_type" : "exact",
           "type" : "simple",
@@ -3329,20 +3171,89 @@
           "with_counters" : false,
           "support_timeout" : false,
           "direct_meters" : null,
-          "action_ids" : [35],
+          "action_ids" : [32],
           "actions" : ["act_3"],
-          "base_default_next" : "node_25",
+          "base_default_next" : "next.hashed",
           "next_tables" : {
-            "act_3" : "node_25"
+            "act_3" : "next.hashed"
           },
           "default_entry" : {
-            "action_id" : 35,
+            "action_id" : 32,
             "action_const" : true,
             "action_data" : [],
             "action_entry_const" : true
           }
         },
         {
+          "name" : "next.hashed",
+          "id" : 13,
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 103,
+            "column" : 10,
+            "source_fragment" : "hashed"
+          },
+          "key" : [
+            {
+              "match_type" : "exact",
+              "target" : ["scalars", "fabric_metadata_t.next_id"],
+              "mask" : null
+            }
+          ],
+          "match_type" : "exact",
+          "type" : "indirect_ws",
+          "action_profile" : "next.ecmp_selector",
+          "max_size" : 1024,
+          "with_counters" : true,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [23, 26, 27, 7],
+          "actions" : ["next.l3_routing", "next.mpls_routing_v4", "next.mpls_routing_v6", "NoAction"],
+          "base_default_next" : "next.broadcast",
+          "next_tables" : {
+            "next.l3_routing" : "next.broadcast",
+            "next.mpls_routing_v4" : "next.broadcast",
+            "next.mpls_routing_v6" : "next.broadcast",
+            "NoAction" : "next.broadcast"
+          }
+        },
+        {
+          "name" : "next.broadcast",
+          "id" : 14,
+          "source_info" : {
+            "filename" : "include/control/next.p4",
+            "line" : 126,
+            "column" : 10,
+            "source_fragment" : "broadcast"
+          },
+          "key" : [
+            {
+              "match_type" : "exact",
+              "target" : ["scalars", "fabric_metadata_t.next_id"],
+              "mask" : null
+            }
+          ],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : true,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [24, 8],
+          "actions" : ["next.set_mcast_group", "NoAction"],
+          "base_default_next" : "node_25",
+          "next_tables" : {
+            "next.set_mcast_group" : "node_25",
+            "NoAction" : "node_25"
+          },
+          "default_entry" : {
+            "action_id" : 8,
+            "action_const" : false,
+            "action_data" : [],
+            "action_entry_const" : false
+          }
+        },
+        {
           "name" : "tbl_act_4",
           "id" : 15,
           "key" : [],
@@ -3352,14 +3263,14 @@
           "with_counters" : false,
           "support_timeout" : false,
           "direct_meters" : null,
-          "action_ids" : [36],
+          "action_ids" : [33],
           "actions" : ["act_4"],
-          "base_default_next" : "next.hashed",
+          "base_default_next" : "node_27",
           "next_tables" : {
-            "act_4" : "next.hashed"
+            "act_4" : "node_27"
           },
           "default_entry" : {
-            "action_id" : 36,
+            "action_id" : 33,
             "action_const" : true,
             "action_data" : [],
             "action_entry_const" : true
@@ -3375,91 +3286,22 @@
           "with_counters" : false,
           "support_timeout" : false,
           "direct_meters" : null,
-          "action_ids" : [37],
+          "action_ids" : [34],
           "actions" : ["act_5"],
-          "base_default_next" : "next.hashed",
+          "base_default_next" : "node_29",
           "next_tables" : {
-            "act_5" : "next.hashed"
+            "act_5" : "node_29"
           },
           "default_entry" : {
-            "action_id" : 37,
+            "action_id" : 34,
             "action_const" : true,
             "action_data" : [],
             "action_entry_const" : true
           }
         },
         {
-          "name" : "next.hashed",
-          "id" : 17,
-          "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 98,
-            "column" : 10,
-            "source_fragment" : "hashed"
-          },
-          "key" : [
-            {
-              "match_type" : "exact",
-              "target" : ["scalars", "fabric_metadata_t.next_id"],
-              "mask" : null
-            }
-          ],
-          "match_type" : "exact",
-          "type" : "indirect_ws",
-          "action_profile" : "next.ecmp_selector",
-          "max_size" : 1024,
-          "with_counters" : false,
-          "support_timeout" : false,
-          "direct_meters" : null,
-          "action_ids" : [27, 29, 30, 9],
-          "actions" : ["next.l3_routing", "next.mpls_routing_v4", "next.mpls_routing_v6", "NoAction"],
-          "base_default_next" : "next.broadcast",
-          "next_tables" : {
-            "next.l3_routing" : "next.broadcast",
-            "next.mpls_routing_v4" : "next.broadcast",
-            "next.mpls_routing_v6" : "next.broadcast",
-            "NoAction" : "next.broadcast"
-          }
-        },
-        {
-          "name" : "next.broadcast",
-          "id" : 18,
-          "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 120,
-            "column" : 10,
-            "source_fragment" : "broadcast"
-          },
-          "key" : [
-            {
-              "match_type" : "exact",
-              "target" : ["scalars", "fabric_metadata_t.next_id"],
-              "mask" : null
-            }
-          ],
-          "match_type" : "exact",
-          "type" : "simple",
-          "max_size" : 1024,
-          "with_counters" : false,
-          "support_timeout" : false,
-          "direct_meters" : null,
-          "action_ids" : [28, 10],
-          "actions" : ["next.set_mcast_group", "NoAction"],
-          "base_default_next" : "node_33",
-          "next_tables" : {
-            "next.set_mcast_group" : "node_33",
-            "NoAction" : "node_33"
-          },
-          "default_entry" : {
-            "action_id" : 10,
-            "action_const" : false,
-            "action_data" : [],
-            "action_entry_const" : false
-          }
-        },
-        {
           "name" : "tbl_act_6",
-          "id" : 19,
+          "id" : 17,
           "key" : [],
           "match_type" : "exact",
           "type" : "simple",
@@ -3467,37 +3309,14 @@
           "with_counters" : false,
           "support_timeout" : false,
           "direct_meters" : null,
-          "action_ids" : [38],
+          "action_ids" : [35],
           "actions" : ["act_6"],
-          "base_default_next" : "node_35",
-          "next_tables" : {
-            "act_6" : "node_35"
-          },
-          "default_entry" : {
-            "action_id" : 38,
-            "action_const" : true,
-            "action_data" : [],
-            "action_entry_const" : true
-          }
-        },
-        {
-          "name" : "tbl_act_7",
-          "id" : 20,
-          "key" : [],
-          "match_type" : "exact",
-          "type" : "simple",
-          "max_size" : 1024,
-          "with_counters" : false,
-          "support_timeout" : false,
-          "direct_meters" : null,
-          "action_ids" : [39],
-          "actions" : ["act_7"],
           "base_default_next" : null,
           "next_tables" : {
-            "act_7" : null
+            "act_6" : null
           },
           "default_entry" : {
-            "action_id" : 39,
+            "action_id" : 35,
             "action_const" : true,
             "action_data" : [],
             "action_entry_const" : true
@@ -3541,7 +3360,7 @@
           "name" : "node_2",
           "id" : 0,
           "source_info" : {
-            "filename" : "./include/control/packetio.p4",
+            "filename" : "include/control/packetio.p4",
             "line" : 25,
             "column" : 12,
             "source_fragment" : "hdr.packet_out.isValid()"
@@ -3549,14 +3368,11 @@
           "expression" : {
             "type" : "expression",
             "value" : {
-              "op" : "==",
-              "left" : {
+              "op" : "d2b",
+              "left" : null,
+              "right" : {
                 "type" : "field",
                 "value" : ["packet_out", "$valid$"]
-              },
-              "right" : {
-                "type" : "hexstr",
-                "value" : "0x01"
               }
             }
           },
@@ -3567,8 +3383,8 @@
           "name" : "node_6",
           "id" : 1,
           "source_info" : {
-            "filename" : "./include/control/forwarding.p4",
-            "line" : 139,
+            "filename" : "include/control/forwarding.p4",
+            "line" : 157,
             "column" : 11,
             "source_fragment" : "fabric_metadata.fwd_type == FWD_BRIDGING"
           },
@@ -3593,8 +3409,8 @@
           "name" : "node_8",
           "id" : 2,
           "source_info" : {
-            "filename" : "./include/control/forwarding.p4",
-            "line" : 140,
+            "filename" : "include/control/forwarding.p4",
+            "line" : 158,
             "column" : 17,
             "source_fragment" : "fabric_metadata.fwd_type == FWD_MPLS"
           },
@@ -3613,40 +3429,14 @@
             }
           },
           "true_next" : "forwarding.mpls",
-          "false_next" : "node_13"
+          "false_next" : "node_11"
         },
         {
-          "name" : "node_10",
+          "name" : "node_11",
           "id" : 3,
           "source_info" : {
-            "filename" : "./include/control/forwarding.p4",
-            "line" : 142,
-            "column" : 16,
-            "source_fragment" : "hdr.ipv4.isValid()"
-          },
-          "expression" : {
-            "type" : "expression",
-            "value" : {
-              "op" : "==",
-              "left" : {
-                "type" : "field",
-                "value" : ["ipv4", "$valid$"]
-              },
-              "right" : {
-                "type" : "hexstr",
-                "value" : "0x01"
-              }
-            }
-          },
-          "true_next" : "tbl_act_0",
-          "false_next" : "tbl_act_1"
-        },
-        {
-          "name" : "node_13",
-          "id" : 4,
-          "source_info" : {
-            "filename" : "./include/control/forwarding.p4",
-            "line" : 150,
+            "filename" : "include/control/forwarding.p4",
+            "line" : 165,
             "column" : 17,
             "source_fragment" : "fabric_metadata.fwd_type == FWD_IPV4_UNICAST"
           },
@@ -3665,14 +3455,14 @@
             }
           },
           "true_next" : "forwarding.unicast_v4",
-          "false_next" : "node_15"
+          "false_next" : "node_13"
         },
         {
-          "name" : "node_15",
-          "id" : 5,
+          "name" : "node_13",
+          "id" : 4,
           "source_info" : {
-            "filename" : "./include/control/forwarding.p4",
-            "line" : 151,
+            "filename" : "include/control/forwarding.p4",
+            "line" : 166,
             "column" : 17,
             "source_fragment" : "fabric_metadata.fwd_type == FWD_IPV4_MULTICAST"
           },
@@ -3691,63 +3481,11 @@
             }
           },
           "true_next" : "forwarding.multicast_v4",
-          "false_next" : "node_17"
-        },
-        {
-          "name" : "node_17",
-          "id" : 6,
-          "source_info" : {
-            "filename" : "./include/control/forwarding.p4",
-            "line" : 152,
-            "column" : 17,
-            "source_fragment" : "fabric_metadata.fwd_type == FWD_IPV6_UNICAST"
-          },
-          "expression" : {
-            "type" : "expression",
-            "value" : {
-              "op" : "==",
-              "left" : {
-                "type" : "field",
-                "value" : ["scalars", "fabric_metadata_t.fwd_type"]
-              },
-              "right" : {
-                "type" : "hexstr",
-                "value" : "0x04"
-              }
-            }
-          },
-          "true_next" : "forwarding.unicast_v6",
-          "false_next" : "node_19"
-        },
-        {
-          "name" : "node_19",
-          "id" : 7,
-          "source_info" : {
-            "filename" : "./include/control/forwarding.p4",
-            "line" : 153,
-            "column" : 17,
-            "source_fragment" : "fabric_metadata.fwd_type == FWD_IPV6_MULTICAST"
-          },
-          "expression" : {
-            "type" : "expression",
-            "value" : {
-              "op" : "==",
-              "left" : {
-                "type" : "field",
-                "value" : ["scalars", "fabric_metadata_t.fwd_type"]
-              },
-              "right" : {
-                "type" : "hexstr",
-                "value" : "0x05"
-              }
-            }
-          },
-          "true_next" : "forwarding.multicast_v6",
           "false_next" : "forwarding.acl"
         },
         {
-          "name" : "node_25",
-          "id" : 8,
+          "name" : "node_19",
+          "id" : 5,
           "expression" : {
             "type" : "expression",
             "value" : {
@@ -3759,92 +3497,67 @@
               }
             }
           },
-          "true_next" : "node_26",
+          "true_next" : "node_20",
           "false_next" : "next.hashed"
         },
         {
-          "name" : "node_26",
-          "id" : 9,
+          "name" : "node_20",
+          "id" : 6,
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 131,
-            "column" : 17,
-            "source_fragment" : "hdr.mpls.isValid()"
+            "filename" : "include/control/next.p4",
+            "line" : 138,
+            "column" : 16,
+            "source_fragment" : "!hdr.mpls.isValid()"
           },
           "expression" : {
             "type" : "expression",
             "value" : {
-              "op" : "!=",
-              "left" : {
-                "type" : "field",
-                "value" : ["mpls", "$valid$"]
-              },
+              "op" : "not",
+              "left" : null,
               "right" : {
-                "type" : "hexstr",
-                "value" : "0x01"
+                "type" : "expression",
+                "value" : {
+                  "op" : "d2b",
+                  "left" : null,
+                  "right" : {
+                    "type" : "field",
+                    "value" : ["mpls", "$valid$"]
+                  }
+                }
               }
             }
           },
-          "true_next" : "node_27",
+          "true_next" : "node_21",
           "false_next" : "next.hashed"
         },
         {
-          "name" : "node_27",
-          "id" : 10,
+          "name" : "node_21",
+          "id" : 7,
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 132,
+            "filename" : "include/control/next.p4",
+            "line" : 139,
             "column" : 19,
             "source_fragment" : "hdr.ipv4.isValid()"
           },
           "expression" : {
             "type" : "expression",
             "value" : {
-              "op" : "==",
-              "left" : {
+              "op" : "d2b",
+              "left" : null,
+              "right" : {
                 "type" : "field",
                 "value" : ["ipv4", "$valid$"]
-              },
-              "right" : {
-                "type" : "hexstr",
-                "value" : "0x01"
               }
             }
           },
-          "true_next" : "tbl_act_4",
-          "false_next" : "node_29"
-        },
-        {
-          "name" : "node_29",
-          "id" : 11,
-          "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 135,
-            "column" : 25,
-            "source_fragment" : "hdr.ipv6.isValid()"
-          },
-          "expression" : {
-            "type" : "expression",
-            "value" : {
-              "op" : "==",
-              "left" : {
-                "type" : "field",
-                "value" : ["ipv6", "$valid$"]
-              },
-              "right" : {
-                "type" : "hexstr",
-                "value" : "0x01"
-              }
-            }
-          },
-          "true_next" : "tbl_act_5",
+          "true_next" : "tbl_act_3",
           "false_next" : "next.hashed"
         },
         {
-          "name" : "node_33",
-          "id" : 12,
+          "name" : "node_25",
+          "id" : 8,
           "source_info" : {
-            "filename" : "./include/control/port_counter.p4",
+            "filename" : "include/control/port_counter.p4",
             "line" : 27,
             "column" : 12,
             "source_fragment" : "standard_metadata.egress_spec < 511"
@@ -3863,14 +3576,14 @@
               }
             }
           },
-          "true_next" : "tbl_act_6",
-          "false_next" : "node_35"
+          "true_next" : "tbl_act_4",
+          "false_next" : "node_27"
         },
         {
-          "name" : "node_35",
-          "id" : 13,
+          "name" : "node_27",
+          "id" : 9,
           "source_info" : {
-            "filename" : "./include/control/port_counter.p4",
+            "filename" : "include/control/port_counter.p4",
             "line" : 30,
             "column" : 12,
             "source_fragment" : "standard_metadata.ingress_port < 511"
@@ -3889,123 +3602,15 @@
               }
             }
           },
-          "false_next" : null,
-          "true_next" : "tbl_act_7"
-        }
-      ]
-    },
-    {
-      "name" : "egress",
-      "id" : 1,
-      "source_info" : {
-        "filename" : "fabric.p4",
-        "line" : 48,
-        "column" : 8,
-        "source_fragment" : "FabricEgress"
-      },
-      "init_table" : "node_39",
-      "tables" : [
-        {
-          "name" : "tbl_act_8",
-          "id" : 21,
-          "key" : [],
-          "match_type" : "exact",
-          "type" : "simple",
-          "max_size" : 1024,
-          "with_counters" : false,
-          "support_timeout" : false,
-          "direct_meters" : null,
-          "action_ids" : [40],
-          "actions" : ["act_8"],
-          "base_default_next" : "tbl_act_10",
-          "next_tables" : {
-            "act_8" : "tbl_act_10"
-          },
-          "default_entry" : {
-            "action_id" : 40,
-            "action_const" : true,
-            "action_data" : [],
-            "action_entry_const" : true
-          }
+          "true_next" : "tbl_act_5",
+          "false_next" : "node_29"
         },
         {
-          "name" : "tbl_act_9",
-          "id" : 22,
-          "key" : [],
-          "match_type" : "exact",
-          "type" : "simple",
-          "max_size" : 1024,
-          "with_counters" : false,
-          "support_timeout" : false,
-          "direct_meters" : null,
-          "action_ids" : [41],
-          "actions" : ["act_9"],
-          "base_default_next" : "tbl_act_10",
-          "next_tables" : {
-            "act_9" : "tbl_act_10"
-          },
-          "default_entry" : {
-            "action_id" : 41,
-            "action_const" : true,
-            "action_data" : [],
-            "action_entry_const" : true
-          }
-        },
-        {
-          "name" : "tbl_act_10",
-          "id" : 23,
-          "key" : [],
-          "match_type" : "exact",
-          "type" : "simple",
-          "max_size" : 1024,
-          "with_counters" : false,
-          "support_timeout" : false,
-          "direct_meters" : null,
-          "action_ids" : [42],
-          "actions" : ["act_10"],
-          "base_default_next" : "node_44",
-          "next_tables" : {
-            "act_10" : "node_44"
-          },
-          "default_entry" : {
-            "action_id" : 42,
-            "action_const" : true,
-            "action_data" : [],
-            "action_entry_const" : true
-          }
-        },
-        {
-          "name" : "tbl_act_11",
-          "id" : 24,
-          "key" : [],
-          "match_type" : "exact",
-          "type" : "simple",
-          "max_size" : 1024,
-          "with_counters" : false,
-          "support_timeout" : false,
-          "direct_meters" : null,
-          "action_ids" : [43],
-          "actions" : ["act_11"],
-          "base_default_next" : null,
-          "next_tables" : {
-            "act_11" : null
-          },
-          "default_entry" : {
-            "action_id" : 43,
-            "action_const" : true,
-            "action_data" : [],
-            "action_entry_const" : true
-          }
-        }
-      ],
-      "action_profiles" : [],
-      "conditionals" : [
-        {
-          "name" : "node_39",
-          "id" : 14,
+          "name" : "node_29",
+          "id" : 10,
           "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 152,
+            "filename" : "include/control/next.p4",
+            "line" : 161,
             "column" : 12,
             "source_fragment" : "fabric_metadata.pop_vlan_at_egress"
           },
@@ -4020,40 +3625,53 @@
               }
             }
           },
-          "true_next" : "node_40",
-          "false_next" : "node_44"
-        },
+          "false_next" : null,
+          "true_next" : "tbl_act_6"
+        }
+      ]
+    },
+    {
+      "name" : "egress",
+      "id" : 1,
+      "source_info" : {
+        "filename" : "fabric.p4",
+        "line" : 60,
+        "column" : 8,
+        "source_fragment" : "FabricEgress"
+      },
+      "init_table" : "node_33",
+      "tables" : [
         {
-          "name" : "node_40",
-          "id" : 15,
-          "source_info" : {
-            "filename" : "./include/control/next.p4",
-            "line" : 153,
-            "column" : 16,
-            "source_fragment" : "hdr.mpls.isValid()"
+          "name" : "tbl_act_7",
+          "id" : 18,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [36],
+          "actions" : ["act_7"],
+          "base_default_next" : null,
+          "next_tables" : {
+            "act_7" : null
           },
-          "expression" : {
-            "type" : "expression",
-            "value" : {
-              "op" : "==",
-              "left" : {
-                "type" : "field",
-                "value" : ["mpls", "$valid$"]
-              },
-              "right" : {
-                "type" : "hexstr",
-                "value" : "0x01"
-              }
-            }
-          },
-          "true_next" : "tbl_act_8",
-          "false_next" : "tbl_act_9"
-        },
+          "default_entry" : {
+            "action_id" : 36,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        }
+      ],
+      "action_profiles" : [],
+      "conditionals" : [
         {
-          "name" : "node_44",
-          "id" : 16,
+          "name" : "node_33",
+          "id" : 11,
           "source_info" : {
-            "filename" : "./include/control/packetio.p4",
+            "filename" : "include/control/packetio.p4",
             "line" : 38,
             "column" : 12,
             "source_fragment" : "standard_metadata.egress_port == CPU_PORT"
@@ -4073,7 +3691,7 @@
             }
           },
           "false_next" : null,
-          "true_next" : "tbl_act_11"
+          "true_next" : "tbl_act_7"
         }
       ]
     }
@@ -4084,14 +3702,36 @@
       "id" : 0,
       "target" : ["ipv4", "hdr_checksum"],
       "type" : "generic",
-      "calculation" : "calc"
+      "calculation" : "calc",
+      "if_cond" : {
+        "type" : "expression",
+        "value" : {
+          "op" : "d2b",
+          "left" : null,
+          "right" : {
+            "type" : "field",
+            "value" : ["ipv4", "$valid$"]
+          }
+        }
+      }
     },
     {
       "name" : "cksum_0",
       "id" : 1,
       "target" : ["ipv4", "hdr_checksum"],
       "type" : "generic",
-      "calculation" : "calc_0"
+      "calculation" : "calc_0",
+      "if_cond" : {
+        "type" : "expression",
+        "value" : {
+          "op" : "d2b",
+          "left" : null,
+          "right" : {
+            "type" : "field",
+            "value" : ["ipv4", "$valid$"]
+          }
+        }
+      }
     }
   ],
   "force_arith" : [],
diff --git a/pipelines/fabric/src/main/resources/p4c-out/bmv2/fabric.p4info b/pipelines/fabric/src/main/resources/p4c-out/bmv2/fabric.p4info
index 20c0dad..80e0880 100644
--- a/pipelines/fabric/src/main/resources/p4c-out/bmv2/fabric.p4info
+++ b/pipelines/fabric/src/main/resources/p4c-out/bmv2/fabric.p4info
@@ -34,7 +34,8 @@
   action_refs {
     id: 16826365
   }
-  const_default_action_id: 16826365
+  const_default_action_id: 16819938
+  direct_resource_ids: 302015144
   size: 1024
 }
 tables {
@@ -65,6 +66,7 @@
     id: 16838162
   }
   const_default_action_id: 16838162
+  direct_resource_ids: 302033694
   size: 1024
 }
 tables {
@@ -92,6 +94,7 @@
     id: 16800567
     annotations: "@defaultonly()"
   }
+  direct_resource_ids: 302047449
   size: 1024
 }
 tables {
@@ -113,6 +116,7 @@
     id: 16800567
     annotations: "@defaultonly()"
   }
+  direct_resource_ids: 302001577
   size: 1024
 }
 tables {
@@ -134,6 +138,7 @@
     id: 16800567
     annotations: "@defaultonly()"
   }
+  direct_resource_ids: 302038636
   size: 1024
 }
 tables {
@@ -161,54 +166,7 @@
     id: 16800567
     annotations: "@defaultonly()"
   }
-  size: 1024
-}
-tables {
-  preamble {
-    id: 33608345
-    name: "forwarding.unicast_v6"
-    alias: "unicast_v6"
-  }
-  match_fields {
-    id: 1
-    name: "hdr.ipv6.dst_addr"
-    bitwidth: 128
-    match_type: LPM
-  }
-  action_refs {
-    id: 16829931
-  }
-  action_refs {
-    id: 16800567
-    annotations: "@defaultonly()"
-  }
-  size: 1024
-}
-tables {
-  preamble {
-    id: 33592333
-    name: "forwarding.multicast_v6"
-    alias: "multicast_v6"
-  }
-  match_fields {
-    id: 1
-    name: "hdr.vlan_tag.vlan_id"
-    bitwidth: 12
-    match_type: EXACT
-  }
-  match_fields {
-    id: 2
-    name: "hdr.ipv6.dst_addr"
-    bitwidth: 128
-    match_type: LPM
-  }
-  action_refs {
-    id: 16829931
-  }
-  action_refs {
-    id: 16800567
-    annotations: "@defaultonly()"
-  }
+  direct_resource_ids: 302009236
   size: 1024
 }
 tables {
@@ -302,6 +260,7 @@
     id: 16819938
   }
   const_default_action_id: 16819938
+  direct_resource_ids: 302000008
   size: 256
 }
 tables {
@@ -326,9 +285,13 @@
     id: 16804266
   }
   action_refs {
+    id: 16841192
+  }
+  action_refs {
     id: 16800567
     annotations: "@defaultonly()"
   }
+  direct_resource_ids: 301991179
   size: 1024
 }
 tables {
@@ -357,6 +320,7 @@
     annotations: "@defaultonly()"
   }
   implementation_id: 285225078
+  direct_resource_ids: 301993193
   size: 1024
 }
 tables {
@@ -378,6 +342,7 @@
     id: 16800567
     annotations: "@defaultonly()"
   }
+  direct_resource_ids: 301995093
   size: 1024
 }
 actions {
@@ -629,6 +594,116 @@
   }
   size: 511
 }
+direct_counters {
+  preamble {
+    id: 302015144
+    name: "filtering.ingress_port_vlan_counter"
+    alias: "ingress_port_vlan_counter"
+  }
+  spec {
+    unit: BOTH
+  }
+  direct_table_id: 33578399
+}
+direct_counters {
+  preamble {
+    id: 302033694
+    name: "filtering.fwd_classifier_counter"
+    alias: "fwd_classifier_counter"
+  }
+  spec {
+    unit: BOTH
+  }
+  direct_table_id: 33619540
+}
+direct_counters {
+  preamble {
+    id: 302047449
+    name: "forwarding.bridging_counter"
+    alias: "bridging_counter"
+  }
+  spec {
+    unit: BOTH
+  }
+  direct_table_id: 33569146
+}
+direct_counters {
+  preamble {
+    id: 302001577
+    name: "forwarding.mpls_counter"
+    alias: "mpls_counter"
+  }
+  spec {
+    unit: BOTH
+  }
+  direct_table_id: 33565386
+}
+direct_counters {
+  preamble {
+    id: 302038636
+    name: "forwarding.unicast_v4_counter"
+    alias: "unicast_v4_counter"
+  }
+  spec {
+    unit: BOTH
+  }
+  direct_table_id: 33589684
+}
+direct_counters {
+  preamble {
+    id: 302009236
+    name: "forwarding.multicast_v4_counter"
+    alias: "multicast_v4_counter"
+  }
+  spec {
+    unit: BOTH
+  }
+  direct_table_id: 33615204
+}
+direct_counters {
+  preamble {
+    id: 302000008
+    name: "forwarding.acl_counter"
+    alias: "acl_counter"
+  }
+  spec {
+    unit: BOTH
+  }
+  direct_table_id: 33587782
+}
+direct_counters {
+  preamble {
+    id: 301991179
+    name: "next.simple_counter"
+    alias: "simple_counter"
+  }
+  spec {
+    unit: BOTH
+  }
+  direct_table_id: 33615740
+}
+direct_counters {
+  preamble {
+    id: 301993193
+    name: "next.hashed_counter"
+    alias: "hashed_counter"
+  }
+  spec {
+    unit: BOTH
+  }
+  direct_table_id: 33569488
+}
+direct_counters {
+  preamble {
+    id: 301995093
+    name: "next.broadcast_counter"
+    alias: "broadcast_counter"
+  }
+  spec {
+    unit: BOTH
+  }
+  direct_table_id: 33608545
+}
 controller_packet_metadata {
   preamble {
     id: 2868941301
diff --git a/pipelines/fabric/src/test/p4/fabric-spgw/.gitignore b/pipelines/fabric/src/test/p4/fabric-spgw/.gitignore
new file mode 100644
index 0000000..16efdec
--- /dev/null
+++ b/pipelines/fabric/src/test/p4/fabric-spgw/.gitignore
@@ -0,0 +1,3 @@
+main.json
+main.p4info
+entries.txt
diff --git a/pipelines/fabric/src/test/p4/fabric-spgw/Makefile b/pipelines/fabric/src/test/p4/fabric-spgw/Makefile
new file mode 100644
index 0000000..71dc23c
--- /dev/null
+++ b/pipelines/fabric/src/test/p4/fabric-spgw/Makefile
@@ -0,0 +1,5 @@
+all:
+	p4c-bm2-ss -o main.json \
+        -DWITH_SPGW -I ../../../main/resources/ \
+        --p4runtime-file main.p4info \
+        --p4runtime-format text ../../../main/resources/fabric.p4
diff --git a/pipelines/fabric/src/test/p4/fabric-spgw/downlink-post.pcap b/pipelines/fabric/src/test/p4/fabric-spgw/downlink-post.pcap
new file mode 100644
index 0000000..a3b76a7d
--- /dev/null
+++ b/pipelines/fabric/src/test/p4/fabric-spgw/downlink-post.pcap
Binary files differ
diff --git a/pipelines/fabric/src/test/p4/fabric-spgw/downlink-pre.pcap b/pipelines/fabric/src/test/p4/fabric-spgw/downlink-pre.pcap
new file mode 100644
index 0000000..465ab03
--- /dev/null
+++ b/pipelines/fabric/src/test/p4/fabric-spgw/downlink-pre.pcap
Binary files differ
diff --git a/pipelines/fabric/src/test/p4/fabric-spgw/entries.json b/pipelines/fabric/src/test/p4/fabric-spgw/entries.json
new file mode 100644
index 0000000..77baf91
--- /dev/null
+++ b/pipelines/fabric/src/test/p4/fabric-spgw/entries.json
@@ -0,0 +1,105 @@
+{
+  "fabric-spgw": {
+    "addr": "localhost:5051",
+    "flows": [
+      {
+        "table_name": "filtering.fwd_classifier",
+        "match_fields": {
+          "standard_metadata.ingress_port": 1,
+          "hdr.ethernet.dst_addr": "c2:42:59:2d:3a:84",
+          "fabric_metadata.original_ether_type": 2048
+        },
+        "action_name": "filtering.set_forwarding_type",
+        "action_params": {
+          "fwd_type": 2
+        }
+      },
+      {
+        "table_name": "forwarding.unicast_v4",
+        "match_fields": {
+          "hdr.ipv4.dst_addr": ["192.168.103.11", 32]
+        },
+        "action_name": "forwarding.set_next_id",
+        "action_params": {
+          "next_id": 1
+        }
+      },
+      {
+        "table_name": "next.simple",
+        "match_fields": {
+          "fabric_metadata.next_id": 1
+        },
+        "action_name": "next.l3_routing",
+        "action_params": {
+          "port_num": 2,
+          "smac": "3a:c1:e2:53:e1:50",
+          "dmac": "52:54:00:29:c9:b7"
+        }
+      },
+      {
+        "table_name": "filtering.fwd_classifier",
+        "match_fields": {
+          "standard_metadata.ingress_port": 2,
+          "hdr.ethernet.dst_addr": "3a:c1:e2:53:e1:50",
+          "fabric_metadata.original_ether_type": 2048
+        },
+        "action_name": "filtering.set_forwarding_type",
+        "action_params": {
+          "fwd_type": 2
+        }
+      },
+      {
+        "table_name": "forwarding.unicast_v4",
+        "match_fields": {
+          "hdr.ipv4.dst_addr": ["16.255.255.252", 32]
+        },
+        "action_name": "forwarding.set_next_id",
+        "action_params": {
+          "next_id": 2
+        }
+      },
+      {
+        "table_name": "next.simple",
+        "match_fields": {
+          "fabric_metadata.next_id": 2
+        },
+        "action_name": "next.l3_routing",
+        "action_params": {
+          "port_num": 1,
+          "smac": "c2:42:59:2d:3a:84",
+          "dmac": "52:54:00:05:7b:59"
+        }
+      },
+      {
+        "table_name": "spgw_ingress.ue_filter_table",
+        "match_fields": {
+          "ipv4.dst_addr": ["16.255.255.252", 32]
+        },
+        "action_name": "NoAction",
+        "action_params": {
+        }
+      },
+      {
+        "table_name": "spgw_ingress.s1u_filter_table",
+        "match_fields": {
+          "spgw_meta.s1u_sgw_addr": "192.168.102.13"
+        },
+        "action_name": "NoAction",
+        "action_params": {
+        }
+      },
+      {
+        "table_name": "spgw_ingress.dl_sess_lookup",
+        "match_fields": {
+          "ipv4.dst_addr": "16.255.255.252"
+        },
+        "action_name": "spgw_ingress.set_dl_sess_info",
+        "action_params": {
+          "teid": 1,
+          "s1u_enb_addr": "192.168.102.11",
+          "s1u_sgw_addr": "192.168.102.13"
+        }
+      }
+    ]
+  }
+}
diff --git a/pipelines/fabric/src/test/p4/fabric-spgw/uplink-post.pcap b/pipelines/fabric/src/test/p4/fabric-spgw/uplink-post.pcap
new file mode 100644
index 0000000..c02e7c6
--- /dev/null
+++ b/pipelines/fabric/src/test/p4/fabric-spgw/uplink-post.pcap
Binary files differ
diff --git a/pipelines/fabric/src/test/p4/fabric-spgw/uplink-pre.pcap b/pipelines/fabric/src/test/p4/fabric-spgw/uplink-pre.pcap
new file mode 100644
index 0000000..78064fa
--- /dev/null
+++ b/pipelines/fabric/src/test/p4/fabric-spgw/uplink-pre.pcap
Binary files differ
diff --git a/pipelines/fabric/src/test/p4/spgw/.gitignore b/pipelines/fabric/src/test/p4/spgw/.gitignore
new file mode 100644
index 0000000..43474cc
--- /dev/null
+++ b/pipelines/fabric/src/test/p4/spgw/.gitignore
@@ -0,0 +1,2 @@
+main.json
+main.p4info
diff --git a/pipelines/fabric/src/test/p4/spgw/Makefile b/pipelines/fabric/src/test/p4/spgw/Makefile
new file mode 100644
index 0000000..6b1e705
--- /dev/null
+++ b/pipelines/fabric/src/test/p4/spgw/Makefile
@@ -0,0 +1,5 @@
+all:
+	p4c-bm2-ss -o main.json \
+        -DWITH_SPGW -I ../../../main/resources/ \
+        --p4runtime-file main.p4info \
+        --p4runtime-format text main.p4
diff --git a/pipelines/fabric/src/test/p4/spgw/downlink-post.pcap b/pipelines/fabric/src/test/p4/spgw/downlink-post.pcap
new file mode 100644
index 0000000..a3b76a7d
--- /dev/null
+++ b/pipelines/fabric/src/test/p4/spgw/downlink-post.pcap
Binary files differ
diff --git a/pipelines/fabric/src/test/p4/spgw/downlink-pre.pcap b/pipelines/fabric/src/test/p4/spgw/downlink-pre.pcap
new file mode 100644
index 0000000..465ab03
--- /dev/null
+++ b/pipelines/fabric/src/test/p4/spgw/downlink-pre.pcap
Binary files differ
diff --git a/pipelines/fabric/src/test/p4/spgw/entries.json b/pipelines/fabric/src/test/p4/spgw/entries.json
new file mode 100644
index 0000000..ea3d201
--- /dev/null
+++ b/pipelines/fabric/src/test/p4/spgw/entries.json
@@ -0,0 +1,37 @@
+{
+  "spgw": {
+    "addr": "localhost:5051",
+    "flows": [
+      {
+        "table_name": "spgw_ingress.ue_filter_table",
+        "match_fields": {
+          "ipv4.dst_addr": ["16.255.255.252", 32]
+        },
+        "action_name": "NoAction",
+        "action_params": {
+        }
+      },
+      {
+        "table_name": "spgw_ingress.s1u_filter_table",
+        "match_fields": {
+          "spgw_meta.s1u_sgw_addr": "192.168.102.13"
+        },
+        "action_name": "NoAction",
+        "action_params": {
+        }
+      },
+      {
+        "table_name": "spgw_ingress.dl_sess_lookup",
+        "match_fields": {
+          "ipv4.dst_addr": "16.255.255.252"
+        },
+        "action_name": "spgw_ingress.set_dl_sess_info",
+        "action_params": {
+          "teid": 1,
+          "s1u_enb_addr": "192.168.102.11",
+          "s1u_sgw_addr": "192.168.102.13"
+        }
+      }
+    ]
+  }
+}
diff --git a/pipelines/fabric/src/test/p4/spgw/main.p4 b/pipelines/fabric/src/test/p4/spgw/main.p4
new file mode 100644
index 0000000..916412c
--- /dev/null
+++ b/pipelines/fabric/src/test/p4/spgw/main.p4
@@ -0,0 +1,148 @@
+/*
+ * 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.
+ *
+ */
+
+#define S1U_PORT 1
+#define SGI_PORT 2
+#define AS_MAC_ADDR  0x52540029c9b7
+#define S1U_MAC_ADDR 0xc242592d3a84
+#define SGI_MAC_ADDR 0x3ac1e253e150
+#define ENB_MAC_ADDR 0x525400057b59
+
+#include <core.p4>
+#include <v1model.p4>
+
+#include "include/define.p4"
+#include "include/header.p4"
+#include "include/checksum.p4"
+#include "include/parser.p4"
+#include "include/control/packetio.p4"
+
+#include "include/spgw.p4"
+
+control table0_control(inout parsed_headers_t hdr,
+                       inout fabric_metadata_t fabric_metadata,
+                       inout standard_metadata_t standard_metadata) {
+
+    action send_to_cpu() {
+        standard_metadata.egress_spec = CPU_PORT;
+        exit;
+    }
+
+    table table0 {
+        key = {
+            standard_metadata.ingress_port : ternary;
+            hdr.ethernet.src_addr          : ternary;
+            hdr.ethernet.dst_addr          : ternary;
+            hdr.ethernet.ether_type        : ternary;
+            hdr.ipv4.src_addr              : ternary;
+            hdr.ipv4.dst_addr              : ternary;
+            hdr.ipv4.protocol              : ternary;
+            fabric_metadata.l4_src_port     : ternary;
+            fabric_metadata.l4_dst_port     : ternary;
+        }
+        actions = {
+            send_to_cpu();
+            NoAction();
+        }
+        const default_action = NoAction();
+    }
+
+    apply {
+        table0.apply();
+    }
+}
+
+//------------------------------------------------------------------------------
+// INGRESS PIPELINE
+//------------------------------------------------------------------------------
+
+control ingress_impl(inout parsed_headers_t hdr,
+                     inout fabric_metadata_t fabric_metadata,
+                     inout standard_metadata_t standard_metadata) {
+
+    apply {
+
+        PacketIoIngress.apply(hdr, fabric_metadata, standard_metadata);
+
+        // Drop garbage, so we get a clean pcap.
+        if (standard_metadata.ingress_port == S1U_PORT
+                && hdr.ethernet.src_addr != ENB_MAC_ADDR) {
+            mark_to_drop();
+            exit;
+        } else if (standard_metadata.ingress_port == SGI_PORT
+                && hdr.ethernet.src_addr != AS_MAC_ADDR) {
+            mark_to_drop();
+            exit;
+        }
+
+        table0_control.apply(hdr, fabric_metadata, standard_metadata);
+
+        if (standard_metadata.egress_spec == CPU_PORT) {
+            return;
+        }
+
+        if (standard_metadata.ingress_port == S1U_PORT) {
+            if (hdr.ethernet.dst_addr != S1U_MAC_ADDR) {
+                mark_to_drop();
+                exit;
+            }
+            standard_metadata.egress_spec = SGI_PORT;
+            hdr.ethernet.src_addr = SGI_MAC_ADDR;
+            hdr.ethernet.dst_addr = AS_MAC_ADDR;
+        } else if (standard_metadata.ingress_port == SGI_PORT) {
+            if (hdr.ethernet.dst_addr != SGI_MAC_ADDR) {
+                mark_to_drop();
+                exit;
+            }
+            standard_metadata.egress_spec = S1U_PORT;
+            hdr.ethernet.src_addr = S1U_MAC_ADDR;
+            hdr.ethernet.dst_addr = ENB_MAC_ADDR;
+        }
+
+#ifdef WITH_SPGW_PCC_GATING
+        fabric_metadata.spgw.l4_src_port = fabric_metadata.l4_src_port;
+        fabric_metadata.spgw.l4_dst_port = fabric_metadata.l4_dst_port;
+#endif // WITH_SPGW_PCC_GATING
+        spgw_ingress.apply(hdr.gtpu_ipv4, hdr.gtpu_udp, hdr.gtpu,
+                           hdr.ipv4, hdr.udp, fabric_metadata.spgw);
+    }
+}
+
+//------------------------------------------------------------------------------
+// EGRESS PIPELINE
+//------------------------------------------------------------------------------
+
+control egress_impl(inout parsed_headers_t hdr,
+                    inout fabric_metadata_t fabric_metadata,
+                    inout standard_metadata_t standard_metadata) {
+    apply {
+        PacketIoEgress.apply(hdr, fabric_metadata, standard_metadata);
+        spgw_egress.apply(hdr.gtpu_ipv4, hdr.gtpu_udp, hdr.gtpu,
+                          fabric_metadata.spgw, standard_metadata);
+     }
+}
+
+//------------------------------------------------------------------------------
+// SWITCH INSTANTIATION
+//------------------------------------------------------------------------------
+
+V1Switch(FabricParser(),
+         FabricVerifyChecksum(),
+         ingress_impl(),
+         egress_impl(),
+         FabricComputeChecksum(),
+         FabricDeparser()) main;
diff --git a/pipelines/fabric/src/test/p4/spgw/uplink-post.pcap b/pipelines/fabric/src/test/p4/spgw/uplink-post.pcap
new file mode 100644
index 0000000..c02e7c6
--- /dev/null
+++ b/pipelines/fabric/src/test/p4/spgw/uplink-post.pcap
Binary files differ
diff --git a/pipelines/fabric/src/test/p4/spgw/uplink-pre.pcap b/pipelines/fabric/src/test/p4/spgw/uplink-pre.pcap
new file mode 100644
index 0000000..78064fa
--- /dev/null
+++ b/pipelines/fabric/src/test/p4/spgw/uplink-pre.pcap
Binary files differ
diff --git a/pom.xml b/pom.xml
index 6523be9..9b68af7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -240,4 +240,67 @@
         </dependencies>
     </dependencyManagement>
 
+    <properties>
+        <errorprone.version>2.2.0</errorprone.version>
+        <betachecker.version>1.0</betachecker.version>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.7.0</version>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                    <compilerId>javac-with-errorprone</compilerId>
+                    <forceJavacCompilerUse>true</forceJavacCompilerUse>
+                    <annotationProcessorPaths>
+                      <path>
+                        <groupId>com.google.guava</groupId>
+                        <artifactId>guava-beta-checker</artifactId>
+                        <version>${betachecker.version}</version>
+                      </path>
+                    </annotationProcessorPaths>
+                    <compilerArgs>
+                      <arg>-Xep:BetaApi:WARN</arg>
+                      <!-- <arg>-Xep:BetaApi:ERROR</arg>  -->
+                      <!-- Add following to disable error-prone -->
+                      <!-- <arg>-XepDisableAllChecks</arg> -->
+                    </compilerArgs>
+                </configuration>
+                <executions>
+                    <execution>
+                      <id>default-testCompile</id>
+                      <phase>test-compile</phase>
+                      <goals>
+                        <goal>testCompile</goal>
+                      </goals>
+                      <configuration>
+                        <!-- Disable Beta Checker for tests -->
+                        <compilerArgs>
+                          <arg>-Xep:BetaApi:OFF</arg>
+                        </compilerArgs>
+                      </configuration>
+                    </execution>
+                </executions>
+                <dependencies>
+                    <dependency>
+                      <groupId>org.codehaus.plexus</groupId>
+                      <artifactId>plexus-compiler-javac-errorprone</artifactId>
+                      <version>2.8.2</version>
+                    </dependency>
+                    <dependency>
+                      <groupId>com.google.errorprone</groupId>
+                      <artifactId>error_prone_core</artifactId>
+                      <!-- override plexus-compiler-javac-errorprone's dependency with the
+                           latest Error Prone version -->
+                      <version>${errorprone.version}</version>
+                    </dependency>
+                </dependencies>
+            </plugin>
+        </plugins>
+    </build>
+
 </project>
diff --git a/protocols/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/BgpPathAttributes.java b/protocols/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/BgpPathAttributes.java
index 6d6455b..f03cdda 100644
--- a/protocols/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/BgpPathAttributes.java
+++ b/protocols/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/BgpPathAttributes.java
@@ -274,7 +274,7 @@
                     BgpErrorType.MISSING_WELLKNOWN_ATTRIBUTE,
                     AsPath.ASPATH_TYPE);
         }
-        if (!isMpUnReach && !isMpReach && !isNextHop) {
+        if (!isMpReach && !isNextHop) {
             log.debug("Mandatory attributes not Present");
             Validation.validateType(BgpErrorType.UPDATE_MESSAGE_ERROR,
                     BgpErrorType.MISSING_WELLKNOWN_ATTRIBUTE,
diff --git a/protocols/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BgpEvpnEsi.java b/protocols/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BgpEvpnEsi.java
index b6816e8..883be89 100644
--- a/protocols/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BgpEvpnEsi.java
+++ b/protocols/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BgpEvpnEsi.java
@@ -16,7 +16,7 @@
 
 package org.onosproject.bgpio.types;
 
-import java.util.Objects;
+import java.util.Arrays;
 import org.jboss.netty.buffer.ChannelBuffer;
 import com.google.common.base.MoreObjects;
 
@@ -78,7 +78,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(ethernetSegmentidentifier);
+        return Arrays.hashCode(ethernetSegmentidentifier);
     };
 
     @Override
@@ -89,9 +89,7 @@
 
         if (obj instanceof BgpEvpnEsi) {
             BgpEvpnEsi that = (BgpEvpnEsi) obj;
-            if (this.ethernetSegmentidentifier == that.ethernetSegmentidentifier) {
-                return true;
-            }
+            return Arrays.equals(this.ethernetSegmentidentifier, that.ethernetSegmentidentifier);
         }
 
         return false;
diff --git a/protocols/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BgpEvpnLabel.java b/protocols/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BgpEvpnLabel.java
index 60348ac..b6a2e87 100644
--- a/protocols/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BgpEvpnLabel.java
+++ b/protocols/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BgpEvpnLabel.java
@@ -16,7 +16,7 @@
 
 package org.onosproject.bgpio.types;
 
-import java.util.Objects;
+import java.util.Arrays;
 import org.jboss.netty.buffer.ChannelBuffer;
 import com.google.common.base.MoreObjects;
 
@@ -87,9 +87,7 @@
 
             BgpEvpnLabel that = (BgpEvpnLabel) obj;
 
-            if (this.mplsLabel == that.mplsLabel) {
-                return true;
-            }
+            return Arrays.equals(this.mplsLabel, that.mplsLabel);
         }
 
         return false;
@@ -97,7 +95,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hashCode(mplsLabel);
+        return Arrays.hashCode(mplsLabel);
     }
 
     @Override
diff --git a/protocols/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/MpReachNlri.java b/protocols/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/MpReachNlri.java
index 106c4bb..578f4fe 100644
--- a/protocols/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/MpReachNlri.java
+++ b/protocols/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/MpReachNlri.java
@@ -447,7 +447,7 @@
             if (listIterator.hasNext()) {
                 tlv1 = listIterator.next();
             }
-            while (listIterator.hasNext()) {
+            while (tlv1 != null && listIterator.hasNext()) {
                 BgpValueType tlv = listIterator.next();
                 if (tlv.getType() != tlv1.getType()) {
                     isAllFlowTypesIdentical = false;
diff --git a/protocols/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/MpUnReachNlri.java b/protocols/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/MpUnReachNlri.java
index e06fb0a..ac63ba7 100644
--- a/protocols/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/MpUnReachNlri.java
+++ b/protocols/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/MpUnReachNlri.java
@@ -402,7 +402,7 @@
             if (listIterator.hasNext()) {
                 tlv1 = listIterator.next();
             }
-            while (listIterator.hasNext()) {
+            while (tlv1 != null && listIterator.hasNext()) {
                 BgpValueType tlv = listIterator.next();
                 if (tlv.getType() != tlv1.getType()) {
                     isAllFlowTypesIdentical = false;
diff --git a/protocols/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/RouteTarget.java b/protocols/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/RouteTarget.java
index 5a009f9..2d48788 100644
--- a/protocols/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/RouteTarget.java
+++ b/protocols/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/RouteTarget.java
@@ -17,7 +17,10 @@
 package org.onosproject.bgpio.types;
 
 import com.google.common.base.MoreObjects;
-import com.google.common.base.Objects;
+
+import java.util.Arrays;
+import java.util.Objects;
+
 import org.jboss.netty.buffer.ChannelBuffer;
 
 /**
@@ -107,7 +110,7 @@
         if (obj instanceof RouteTarget) {
             RouteTarget that = (RouteTarget) obj;
             if (this.type == that.type
-                    && this.routeTarget == that.routeTarget) {
+                    && Arrays.equals(this.routeTarget, that.routeTarget)) {
                 return true;
             }
         }
@@ -116,7 +119,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hashCode(routeTarget);
+        return Objects.hash(type, Arrays.hashCode(routeTarget));
     }
 
     @Override
diff --git a/protocols/gnmi/stub/src/main/proto/COMMIT_ID b/protocols/gnmi/stub/src/main/proto/COMMIT_ID
index 0ac43f2..acf773d 100644
--- a/protocols/gnmi/stub/src/main/proto/COMMIT_ID
+++ b/protocols/gnmi/stub/src/main/proto/COMMIT_ID
@@ -1,2 +1,2 @@
 https://github.com/openconfig/gnmi/blob/master/proto/gnmi/gnmi.proto
-8a14ac0e9ed67e08988f9913243d89f398454824
+9c8d9e965b3e854107ea02c12ab11b70717456f2
diff --git a/protocols/gnmi/stub/src/main/proto/gnmi.proto b/protocols/gnmi/stub/src/main/proto/gnmi.proto
index d497dc0..1f3bb7c 100644
--- a/protocols/gnmi/stub/src/main/proto/gnmi.proto
+++ b/protocols/gnmi/stub/src/main/proto/gnmi.proto
@@ -25,7 +25,7 @@
 // tree supported by a device ("target").
 //
 // This document references the gNMI Specification which can be found at
-// http://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi.md
+// http://github.com/openconfig/reference/blob/master/rpc/gnmi
 package gnmi;
 
 // Define a protobuf FileOption that defines the gNMI service version.
@@ -36,7 +36,7 @@
 
 // gNMI_service is the current version of the gNMI service, returned through
 // the Capabilities RPC.
-option (gnmi_service) = "0.4.0";
+option (gnmi_service) = "0.5.0";
 
 service gNMI {
     // Capabilities allows the client to retrieve the set of capabilities that
@@ -90,6 +90,7 @@
     Path path = 1;                      // The path (key) for the update.
     Value value = 2 [deprecated=true];  // The value (value) for the update.
     TypedValue val = 3;                 // The explicitly typed update value.
+    uint32 duplicates = 4;              // Number of coalesced duplicates.
 }
 
 // TypedValue is used to encode a value being sent between the client and
@@ -126,6 +127,8 @@
     repeated string element = 1 [deprecated=true];
     string origin = 2;                              // Label to disambiguate path.
     repeated PathElem elem = 3;                     // Elements of the path.
+    string target = 4;                              // The name of the target
+    // (Sec. 2.2.2.1)
 }
 
 // PathElem encodes an element of a gNMI path, along ith any attributes (keys)
@@ -173,7 +176,7 @@
 // is expressed as a set of digits with the precision specifying the
 // number of digits following the decimal point in the digit set.
 message Decimal64 {
-    uint64 digits = 1;        // Set of digits.
+    int64 digits = 1;         // Set of digits.
     uint32 precision = 2;     // Number of digits following the decimal point.
 }
 
@@ -250,6 +253,12 @@
     // The encoding that the target should use within the Notifications generated
     // corresponding to the SubscriptionList.
     Encoding encoding = 8;
+    // An optional field to specify that only updates to current state should be
+    // sent to a client. If set, the initial state is not sent to the client but
+    // rather only the sync message followed by any subsequent updates to the
+    // current state. For ONCE and POLL modes, this causes the server to send only
+    // the sync message (Sec. 3.5.2.3).
+    bool updates_only = 9;
 }
 
 // Subscription is a single request within a SubscriptionList. The path
diff --git a/protocols/isis/ctl/src/main/java/org/onosproject/isis/controller/impl/IsisChannelHandler.java b/protocols/isis/ctl/src/main/java/org/onosproject/isis/controller/impl/IsisChannelHandler.java
index b25c2ef..da0c946 100644
--- a/protocols/isis/ctl/src/main/java/org/onosproject/isis/controller/impl/IsisChannelHandler.java
+++ b/protocols/isis/ctl/src/main/java/org/onosproject/isis/controller/impl/IsisChannelHandler.java
@@ -217,12 +217,9 @@
         if (message instanceof List) {
             List<IsisMessage> isisMessageList = (List<IsisMessage>) message;
             log.debug("IsisChannelHandler::List of IsisMessages Size {}", isisMessageList.size());
-            if (isisMessageList != null) {
-                for (IsisMessage isisMessage : isisMessageList) {
-                    processIsisMessage(isisMessage, ctx);
-                }
-            } else {
-                log.debug("IsisChannelHandler::IsisMessages Null List...!!");
+
+            for (IsisMessage isisMessage : isisMessageList) {
+                processIsisMessage(isisMessage, ctx);
             }
         }
         if (message instanceof IsisMessage) {
diff --git a/protocols/isis/isisio/src/main/java/org/onosproject/isis/io/util/ChecksumCalculator.java b/protocols/isis/isisio/src/main/java/org/onosproject/isis/io/util/ChecksumCalculator.java
index c4dbd90..0671eed 100644
--- a/protocols/isis/isisio/src/main/java/org/onosproject/isis/io/util/ChecksumCalculator.java
+++ b/protocols/isis/isisio/src/main/java/org/onosproject/isis/io/util/ChecksumCalculator.java
@@ -57,14 +57,12 @@
         tempLsaByte[lspChecksumPos1] = 0;
         tempLsaByte[lspChecksumPos2] = 0;
         byte[] byteCheckSum = {0, 0};
-        if (lspBytes != null) {
-            for (int i = 12; i < tempLsaByte.length; i++) {
-                checksumOut[0] = checksumOut[0] + ((int) tempLsaByte[i] & 0xFF);
-                checksumOut[1] = checksumOut[1] + checksumOut[0];
-            }
-            checksumOut[0] = checksumOut[0] % 255;
-            checksumOut[1] = checksumOut[1] % 255;
+        for (int i = 12; i < tempLsaByte.length; i++) {
+            checksumOut[0] = checksumOut[0] + ((int) tempLsaByte[i] & 0xFF);
+            checksumOut[1] = checksumOut[1] + checksumOut[0];
         }
+        checksumOut[0] = checksumOut[0] % 255;
+        checksumOut[1] = checksumOut[1] % 255;
         int byte1 = (int) ((tempLsaByte.length - lspChecksumPos1 - 1) * checksumOut[0] - checksumOut[1]) % 255;
         if (byte1 <= 0) {
             byte1 += 255;
diff --git a/protocols/lisp/msg/src/test/java/org/onosproject/lisp/msg/protocols/DefaultLispMapReplyTest.java b/protocols/lisp/msg/src/test/java/org/onosproject/lisp/msg/protocols/DefaultLispMapReplyTest.java
index e984ced..8bd7b2e 100644
--- a/protocols/lisp/msg/src/test/java/org/onosproject/lisp/msg/protocols/DefaultLispMapReplyTest.java
+++ b/protocols/lisp/msg/src/test/java/org/onosproject/lisp/msg/protocols/DefaultLispMapReplyTest.java
@@ -27,6 +27,8 @@
 import org.onosproject.lisp.msg.exceptions.LispWriterException;
 import org.onosproject.lisp.msg.protocols.DefaultLispMapReply.ReplyReader;
 import org.onosproject.lisp.msg.protocols.DefaultLispMapReply.ReplyWriter;
+import org.onosproject.lisp.msg.protocols.LispMapRecord.MapRecordBuilder;
+import org.onosproject.lisp.msg.protocols.LispMapReply.ReplyBuilder;
 import org.onosproject.lisp.msg.types.LispIpv4Address;
 
 import java.util.List;
@@ -34,9 +36,7 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
 import static org.onosproject.lisp.msg.protocols.DefaultLispMapRecord.DefaultMapRecordBuilder;
-import static org.onosproject.lisp.msg.protocols.DefaultLispMapRecord.MapRecordBuilder;
 import static org.onosproject.lisp.msg.protocols.DefaultLispMapReply.DefaultReplyBuilder;
-import static org.onosproject.lisp.msg.protocols.DefaultLispMapReply.ReplyBuilder;
 
 /**
  * Unit tests for DefaultLispMapReply class.
diff --git a/protocols/netconf/api/src/main/java/org/onosproject/netconf/NetconfDeviceInfo.java b/protocols/netconf/api/src/main/java/org/onosproject/netconf/NetconfDeviceInfo.java
index d05acc3..3ebb982 100644
--- a/protocols/netconf/api/src/main/java/org/onosproject/netconf/NetconfDeviceInfo.java
+++ b/protocols/netconf/api/src/main/java/org/onosproject/netconf/NetconfDeviceInfo.java
@@ -67,7 +67,7 @@
     public NetconfDeviceInfo(String name, String password, IpAddress ipAddress,
                              int port) {
         checkArgument(!name.equals(""), "Empty device username");
-        checkNotNull(port > 0, "Negative port");
+        checkArgument(port > 0, "Negative port");
         checkNotNull(ipAddress, "Null ip address");
         this.name = name;
         this.password = password;
@@ -96,7 +96,7 @@
     public NetconfDeviceInfo(String name, String password, IpAddress ipAddress,
                              int port, String keyString) {
         checkArgument(!name.equals(""), "Empty device name");
-        checkNotNull(port > 0, "Negative port");
+        checkArgument(port > 0, "Negative port");
         checkNotNull(ipAddress, "Null ip address");
         this.name = name;
         this.password = password;
@@ -116,7 +116,7 @@
      */
     public NetconfDeviceInfo(NetconfDeviceConfig netconfConfig) {
         checkArgument(!netconfConfig.username().isEmpty(), "Empty device name");
-        checkNotNull(netconfConfig.port() > 0, "Negative port");
+        checkArgument(netconfConfig.port() > 0, "Negative port");
         checkNotNull(netconfConfig.ip(), "Null ip address");
 
         this.name = netconfConfig.username();
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/DefaultOpenFlowPacketContext.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/DefaultOpenFlowPacketContext.java
index 9bd2146..ac9ed82 100644
--- a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/DefaultOpenFlowPacketContext.java
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/DefaultOpenFlowPacketContext.java
@@ -105,13 +105,17 @@
         checkPermission(PACKET_READ);
 
         try {
-            return Ethernet.deserializer().deserialize(pktin.getData(), 0, pktin.getData().length);
+            return Ethernet.deserializer().deserialize(
+                    pktin.getData(), 0, pktin.getData().length);
         } catch (BufferUnderflowException | NullPointerException |
                 DeserializationException e) {
             Logger log = LoggerFactory.getLogger(getClass());
-            log.error("packet deserialization problem : {}", e.getMessage());
-            return null;
+            log.error("Packet deserialization problem", e);
+        } catch (Exception e) {
+            Logger log = LoggerFactory.getLogger(getClass());
+            log.error("Unexpected packet deserialization problem", e);
         }
+        return null;
     }
 
     @Override
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowOpticalSwitch.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowOpticalSwitch.java
index eac7dc2..d3b11bb 100644
--- a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowOpticalSwitch.java
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowOpticalSwitch.java
@@ -31,7 +31,7 @@
 public interface OpenFlowOpticalSwitch extends OpenFlowSwitch, WithTypedPorts {
 
     // OpenFlowOpticalSwitch only returns Ethernet ports.
-    // This is a limitation due to issue described in ONOS-3796.
+    // This is a limitation due to issue described in ONOS-3736.
     // This method should return all port type once the limitation is fixed.
     /**
      * Returns a list of standard (Ethernet) ports.
@@ -40,7 +40,7 @@
      */
     @Beta
     @Override
-    abstract List<OFPortDesc> getPorts();
+    List<OFPortDesc> getPorts();
 
     /**
      * Returns updated PortDescriptions built from experimenter message
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/ThirdPartyMessage.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/ThirdPartyMessage.java
index b00da85..dbb1d53 100644
--- a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/ThirdPartyMessage.java
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/ThirdPartyMessage.java
@@ -75,7 +75,7 @@
 
     @Override
     public int hashCodeIgnoreXid() {
-        return payLoad.hashCode();
+        return Arrays.hashCode(payLoad);
     }
 
     @Override
diff --git a/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OFChannelHandler.java b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OFChannelHandler.java
index 769fa2d..414bd89 100644
--- a/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OFChannelHandler.java
+++ b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OFChannelHandler.java
@@ -380,8 +380,9 @@
                     h.portDescReplies.add((OFPortDescStatsReply) m);
                 }
                 //h.portDescReply = (OFPortDescStatsReply) m; // temp store
-                log.info("Received port desc reply for switch at {}",
-                        h.getSwitchInfoString());
+                log.debug("Received port desc reply for switch at {}: {}",
+                         h.getSwitchInfoString(),
+                         ((OFPortDescStatsReply) m).getEntries());
                 try {
                     h.sendHandshakeSetConfig();
                 } catch (IOException e) {
@@ -780,6 +781,9 @@
             void processOFStatisticsReply(OFChannelHandler h,
                     OFStatsReply m) {
                 if (m.getStatsType().equals(OFStatsType.PORT_DESC)) {
+                    log.debug("Received port desc message from {}: {}",
+                             h.sw.getDpid(),
+                             ((OFPortDescStatsReply) m).getEntries());
                     h.sw.setPortDescReply((OFPortDescStatsReply) m);
                 }
                 h.dispatchMessage(m);
@@ -1443,7 +1447,7 @@
         if (dispatcherHandle.isDone()) {
             // dispatcher terminated for some reason, restart
 
-            dispatcherHandle = dispatcher.submit(() -> {
+            dispatcherHandle = dispatcher.submit((Runnable) () -> {
                 try {
                     List<OFMessage> msgs = new ArrayList<>();
                     for (;;) {
diff --git a/protocols/ospf/ctl/src/main/java/org/onosproject/ospf/controller/area/OspfInterfaceImpl.java b/protocols/ospf/ctl/src/main/java/org/onosproject/ospf/controller/area/OspfInterfaceImpl.java
index 79b613a..7c1626b 100644
--- a/protocols/ospf/ctl/src/main/java/org/onosproject/ospf/controller/area/OspfInterfaceImpl.java
+++ b/protocols/ospf/ctl/src/main/java/org/onosproject/ospf/controller/area/OspfInterfaceImpl.java
@@ -138,6 +138,7 @@
      *
      * @return OSPF area instance
      */
+    @Override
     public OspfArea ospfArea() {
         return ospfArea;
     }
@@ -156,6 +157,7 @@
      *
      * @param ospfArea OSPF area instance
      */
+    @Override
     public void setOspfArea(OspfArea ospfArea) {
         this.ospfArea = ospfArea;
     }
@@ -188,6 +190,7 @@
      *
      * @return network mask
      */
+    @Override
     public Ip4Address ipNetworkMask() {
         return ipNetworkMask;
     }
@@ -207,6 +210,7 @@
      *
      * @param ospfNbr ospfNbr instance
      */
+    @Override
     public void addNeighbouringRouter(OspfNbr ospfNbr) {
         listOfNeighbors.put(ospfNbr.neighborId().toString(), ospfNbr);
     }
@@ -217,6 +221,7 @@
      * @param neighborId neighbors id
      * @return ospfNbr neighbor instance
      */
+    @Override
     public OspfNbr neighbouringRouter(String neighborId) {
         return listOfNeighbors.get(neighborId);
     }
@@ -224,6 +229,7 @@
     /**
      * Removes all the neighbors.
      */
+    @Override
     public void removeNeighbors() {
         Set<String> neighbors = listOfNeighbors.keySet();
         for (String neighborId : neighbors) {
@@ -245,7 +251,7 @@
         ospfNeighbor.stopRxMtDdTimer();
         ospfNeighbor.stopRxMtLsrTimer();
 
-        listOfNeighbors.remove(ospfNeighbor.neighborId());
+        listOfNeighbors.remove(ospfNeighbor.neighborId().toString());
     }
 
 
@@ -274,6 +280,7 @@
      *
      * @param lsaKey key used to store LSA in map
      */
+    @Override
     public void removeLsaFromNeighborMap(String lsaKey) {
         listOfNeighborMap.remove(lsaKey);
     }
@@ -284,6 +291,7 @@
      * @param neighborId neighbors id
      * @return true if neighbor in list else false
      */
+    @Override
     public boolean isNeighborInList(String neighborId) {
         return listOfNeighbors.containsKey(neighborId);
     }
@@ -293,6 +301,7 @@
      *
      * @return listOfNeighbors as key value pair
      */
+    @Override
     public Map<String, OspfNbr> listOfNeighbors() {
         return listOfNeighbors;
     }
@@ -311,6 +320,7 @@
      *
      * @return interface index
      */
+    @Override
     public int interfaceIndex() {
         return interfaceIndex;
     }
@@ -320,6 +330,7 @@
      *
      * @param interfaceIndex interface index
      */
+    @Override
     public void setInterfaceIndex(int interfaceIndex) {
         this.interfaceIndex = interfaceIndex;
     }
@@ -329,6 +340,7 @@
      *
      * @return IP address
      */
+    @Override
     public Ip4Address ipAddress() {
         return ipAddress;
     }
@@ -338,6 +350,7 @@
      *
      * @param ipAddress interface IP address
      */
+    @Override
     public void setIpAddress(Ip4Address ipAddress) {
         this.ipAddress = ipAddress;
     }
@@ -347,6 +360,7 @@
      *
      * @return routerPriority value
      */
+    @Override
     public int routerPriority() {
         return routerPriority;
     }
@@ -356,6 +370,7 @@
      *
      * @param routerPriority value
      */
+    @Override
     public void setRouterPriority(int routerPriority) {
         this.routerPriority = routerPriority;
     }
@@ -365,6 +380,7 @@
      *
      * @return hello interval time
      */
+    @Override
     public int helloIntervalTime() {
         return helloIntervalTime;
     }
@@ -374,6 +390,7 @@
      *
      * @param helloIntervalTime an integer interval time
      */
+    @Override
     public void setHelloIntervalTime(int helloIntervalTime) {
         this.helloIntervalTime = helloIntervalTime;
     }
@@ -383,6 +400,7 @@
      *
      * @return router dead interval time
      */
+    @Override
     public int routerDeadIntervalTime() {
         return routerDeadIntervalTime;
     }
@@ -392,6 +410,7 @@
      *
      * @param routerDeadIntervalTime router dead interval time
      */
+    @Override
     public void setRouterDeadIntervalTime(int routerDeadIntervalTime) {
         this.routerDeadIntervalTime = routerDeadIntervalTime;
     }
@@ -401,6 +420,7 @@
      *
      * @return interfaceType an integer represents interface type
      */
+    @Override
     public int interfaceType() {
         return interfaceType;
     }
@@ -410,6 +430,7 @@
      *
      * @param interfaceType interface type
      */
+    @Override
     public void setInterfaceType(int interfaceType) {
         this.interfaceType = interfaceType;
     }
@@ -419,6 +440,7 @@
      *
      * @return mtu an integer represents max transfer unit
      */
+    @Override
     public int mtu() {
         return mtu;
     }
@@ -428,6 +450,7 @@
      *
      * @param mtu max transfer unit
      */
+    @Override
     public void setMtu(int mtu) {
         this.mtu = mtu;
     }
@@ -437,6 +460,7 @@
      *
      * @return retransmit interval
      */
+    @Override
     public int reTransmitInterval() {
         return reTransmitInterval;
     }
@@ -446,6 +470,7 @@
      *
      * @param reTransmitInterval retransmit interval
      */
+    @Override
     public void setReTransmitInterval(int reTransmitInterval) {
         this.reTransmitInterval = reTransmitInterval;
     }
@@ -455,6 +480,7 @@
      *
      * @return dr designated routers IP address
      */
+    @Override
     public Ip4Address dr() {
         return dr;
     }
@@ -464,6 +490,7 @@
      *
      * @param dr designated routers IP address
      */
+    @Override
     public void setDr(Ip4Address dr) {
         this.dr = dr;
     }
@@ -473,6 +500,7 @@
      *
      * @return bdr backup designated routers IP address
      */
+    @Override
     public Ip4Address bdr() {
         return bdr;
     }
@@ -482,6 +510,7 @@
      *
      * @param bdr backup designated routers IP address
      */
+    @Override
     public void setBdr(Ip4Address bdr) {
         this.bdr = bdr;
     }
@@ -491,6 +520,7 @@
      *
      * @throws Exception might throws exception
      */
+    @Override
     public void interfaceUp() throws Exception {
         log.debug("OSPFInterfaceChannelHandler::interfaceUp...!!!");
         if (interfaceType() == OspfInterfaceType.POINT_TO_POINT.value()) {
@@ -578,6 +608,7 @@
      * All interface variables are reset, and interface timers disabled.
      * Also all neighbor connections associated with the interface are destroyed.
      */
+    @Override
     public void interfaceDown() {
         log.debug("OSPFInterfaceChannelHandler::interfaceDown ");
         stopHelloTimer();
@@ -594,6 +625,7 @@
      * @param ctx         channel handler context instance.
      * @throws Exception might throws exception
      */
+    @Override
     public void processOspfMessage(OspfMessage ospfMessage, ChannelHandlerContext ctx) throws Exception {
         log.debug("OspfChannelHandler::processOspfMessage...!!!");
 
@@ -1078,7 +1110,7 @@
                             OspfUtil.LSA_HEADER_LENGTH; // subtract a normal IP header.
                     int noLsa = 0;
                     while (listItr.hasNext()) {
-                        LsRequestPacket lsRequest = (LsRequestPacket) listItr.next();
+                        LsRequestPacket lsRequest = listItr.next();
                         // to verify length of the LSA
                         LsaWrapper wrapper = ospfArea.getLsa(lsRequest.lsType(), lsRequest.linkStateId(),
                                                              lsRequest.ownRouterId());
@@ -1168,7 +1200,7 @@
                     LsaHeader lsRequest = (LsaHeader) itr.next();
 
                     OspfLsa ospfLsa =
-                            (OspfLsa) nbr.getPendingReTxList().get(((OspfAreaImpl) ospfArea).getLsaKey(lsRequest));
+                            nbr.getPendingReTxList().get(((OspfAreaImpl) ospfArea).getLsaKey(lsRequest));
                     if (ospfLsa != null) {
                         String isSame = ((OspfLsdbImpl) ospfArea.database()).isNewerOrSameLsa(
                                 lsRequest, (LsaHeader) ospfLsa);
@@ -1206,6 +1238,7 @@
     /**
      * Starts the hello timer which sends hello packet every configured seconds.
      */
+    @Override
     public void startHelloTimer() {
         log.debug("OSPFInterfaceChannelHandler::startHelloTimer");
         exServiceHello = Executors.newSingleThreadScheduledExecutor();
@@ -1217,6 +1250,7 @@
     /**
      * Stops the hello timer.
      */
+    @Override
     public void stopHelloTimer() {
         log.debug("OSPFInterfaceChannelHandler::stopHelloTimer ");
         exServiceHello.shutdown();
@@ -1244,6 +1278,7 @@
     /**
      * Starts the timer which waits for configured seconds and sends Delayed Ack Packet.
      */
+    @Override
     public void startDelayedAckTimer() {
         if (!isDelayedAckTimerScheduled) {
             log.debug("Started DelayedAckTimer...!!!");
@@ -1259,6 +1294,7 @@
     /**
      * Stops the delayed acknowledge timer.
      */
+    @Override
     public void stopDelayedAckTimer() {
         if (isDelayedAckTimerScheduled) {
             log.debug("Stopped DelayedAckTimer...!!!");
diff --git a/protocols/ospf/ctl/src/main/java/org/onosproject/ospf/controller/impl/OspfInterfaceChannelHandler.java b/protocols/ospf/ctl/src/main/java/org/onosproject/ospf/controller/impl/OspfInterfaceChannelHandler.java
index bba5c77..1a47a0c 100644
--- a/protocols/ospf/ctl/src/main/java/org/onosproject/ospf/controller/impl/OspfInterfaceChannelHandler.java
+++ b/protocols/ospf/ctl/src/main/java/org/onosproject/ospf/controller/impl/OspfInterfaceChannelHandler.java
@@ -220,12 +220,9 @@
         if (message instanceof List) {
             List<OspfMessage> ospfMessageList = (List<OspfMessage>) message;
             log.debug("OspfChannelHandler::List of IsisMessages Size {}", ospfMessageList.size());
-            if (ospfMessageList != null) {
-                for (OspfMessage ospfMessage : ospfMessageList) {
-                    processOspfMessage(ospfMessage, ctx);
-                }
-            } else {
-                log.debug("OspfChannelHandler::OspfMessages Null List...!!");
+
+            for (OspfMessage ospfMessage : ospfMessageList) {
+                processOspfMessage(ospfMessage, ctx);
             }
         }
         if (message instanceof OspfMessage) {
diff --git a/protocols/ospf/ctl/src/main/java/org/onosproject/ospf/controller/impl/OspfNbrImpl.java b/protocols/ospf/ctl/src/main/java/org/onosproject/ospf/controller/impl/OspfNbrImpl.java
index 68ff4fc..ff3f75b 100644
--- a/protocols/ospf/ctl/src/main/java/org/onosproject/ospf/controller/impl/OspfNbrImpl.java
+++ b/protocols/ospf/ctl/src/main/java/org/onosproject/ospf/controller/impl/OspfNbrImpl.java
@@ -218,6 +218,7 @@
      *
      * @return the IP address of this neighbor
      */
+    @Override
     public Ip4Address neighborIpAddr() {
         return neighborIpAddr;
     }
@@ -227,6 +228,7 @@
      *
      * @return true if the neighbor is opaque enabled else false.
      */
+    @Override
     public boolean isOpaqueCapable() {
         return isOpaqueCapable;
     }
@@ -236,6 +238,7 @@
      *
      * @param isOpaqueCapable true if the neighbor is opaque enabledelse false
      */
+    @Override
     public void setIsOpaqueCapable(boolean isOpaqueCapable) {
         this.isOpaqueCapable = isOpaqueCapable;
     }
@@ -245,6 +248,7 @@
      *
      * @param routerDeadInterval router dead interval
      */
+    @Override
     public void setRouterDeadInterval(int routerDeadInterval) {
         this.routerDeadInterval = routerDeadInterval;
     }
@@ -386,7 +390,7 @@
             state = OspfNeighborState.EXCHANGE;
             boolean excludeMaxAgeLsa = true;
             //list of contents of area wise LSA
-            ddSummaryList = (CopyOnWriteArrayList) ospfArea.getLsaHeaders(excludeMaxAgeLsa, isOpaqueCapable);
+            ddSummaryList = ospfArea.getLsaHeaders(excludeMaxAgeLsa, isOpaqueCapable);
 
             if (neighborIsMaster) {
                 processLsas(payload);
@@ -558,6 +562,7 @@
      * @param ch netty channel instance
      * @throws Exception on error
      */
+    @Override
     public void badLSReq(Channel ch) throws Exception {
         log.debug("OSPFNbr::badLSReq...!!!");
 
@@ -837,6 +842,7 @@
      *
      * @param ch netty channel instance
      */
+    @Override
     public void adjOk(Channel ch) {
         log.debug("OSPFNbr::adjOk...!!!");
         if (ospfInterface.interfaceType() != OspfInterfaceType.POINT_TO_POINT.value()) {
@@ -1176,7 +1182,7 @@
             }
         }
         // RFC 2328 Section 13  (6)
-        if (lsReqList.contains(key)) {
+        if (lsReqList.containsValue(key)) {
             badLSReq(ch);
         }
         if (status.equals("same")) { //13 (7)
@@ -1397,6 +1403,7 @@
     /**
      * Starts the inactivity timer.
      */
+    @Override
     public void startInactivityTimeCheck() {
         if (!inActivityTimerScheduled) {
             log.debug("OSPFNbr::startInactivityTimeCheck");
@@ -1411,6 +1418,7 @@
     /**
      * Stops the inactivity timer.
      */
+    @Override
     public void stopInactivityTimeCheck() {
         if (inActivityTimerScheduled) {
             log.debug("OSPFNbr::stopInactivityTimeCheck ");
@@ -1440,6 +1448,7 @@
     /**
      * Stops the flooding timer.
      */
+    @Override
     public void stopFloodingTimer() {
         if (floodingTimerScheduled) {
             log.debug("OSPFNbr::stopFloodingTimer ");
@@ -1467,6 +1476,7 @@
     /**
      * Stops the Dd Retransmission executor task.
      */
+    @Override
     public void stopRxMtDdTimer() {
         if (rxmtDdPacketTimerScheduled) {
             exServiceRxmtDDPacket.shutdown();
@@ -1494,6 +1504,7 @@
     /**
      * Stops Ls request retransmission executor task.
      */
+    @Override
     public void stopRxMtLsrTimer() {
         if (rxmtLsrTimerScheduled) {
             exServiceRxmtLsr.shutdown();
@@ -1524,6 +1535,7 @@
      *
      * @return neighbor id
      */
+    @Override
     public Ip4Address neighborId() {
         return neighborId;
     }
@@ -1533,6 +1545,7 @@
      *
      * @param neighborId neighbor id
      */
+    @Override
     public void setNeighborId(Ip4Address neighborId) {
         this.neighborId = neighborId;
     }
@@ -1542,6 +1555,7 @@
      *
      * @return neighbor DR address
      */
+    @Override
     public Ip4Address neighborDr() {
         return neighborDr;
     }
@@ -1551,6 +1565,7 @@
      *
      * @param neighborDr neighbor DR address
      */
+    @Override
     public void setNeighborDr(Ip4Address neighborDr) {
         this.neighborDr = neighborDr;
     }
@@ -1560,6 +1575,7 @@
      *
      * @return neighbor BDR address
      */
+    @Override
     public Ip4Address neighborBdr() {
         return neighborBdr;
     }
@@ -1569,6 +1585,7 @@
      *
      * @param neighborBdr neighbor BDR address
      */
+    @Override
     public void setNeighborBdr(Ip4Address neighborBdr) {
         this.neighborBdr = neighborBdr;
     }
@@ -1578,6 +1595,7 @@
      *
      * @return router priority
      */
+    @Override
     public int routerPriority() {
         return routerPriority;
     }
@@ -1587,6 +1605,7 @@
      *
      * @param routerPriority router priority
      */
+    @Override
     public void setRouterPriority(int routerPriority) {
         this.routerPriority = routerPriority;
     }
@@ -1596,6 +1615,7 @@
      *
      * @return options value
      */
+    @Override
     public int options() {
         return options;
     }
@@ -1605,6 +1625,7 @@
      *
      * @param options options value
      */
+    @Override
     public void setOptions(int options) {
         this.options = options;
     }
@@ -1614,6 +1635,7 @@
      *
      * @return DD sequence number
      */
+    @Override
     public long ddSeqNum() {
         return ddSeqNum;
     }
@@ -1623,6 +1645,7 @@
      *
      * @param ddSeqNum DD sequence number
      */
+    @Override
     public void setDdSeqNum(long ddSeqNum) {
         this.ddSeqNum = ddSeqNum;
     }
@@ -1632,6 +1655,7 @@
      *
      * @return true if neighbor is master else false
      */
+    @Override
     public int isMaster() {
         return isMaster;
     }
@@ -1677,6 +1701,7 @@
      *
      * @return neighbors state
      */
+    @Override
     public OspfNeighborState getState() {
         return state;
     }
@@ -1695,6 +1720,7 @@
      *
      * @param isMaster neighbor is master or not
      */
+    @Override
     public void setIsMaster(int isMaster) {
         this.isMaster = isMaster;
     }
@@ -1704,6 +1730,7 @@
      *
      * @return ls request list
      */
+    @Override
     public Hashtable getLsReqList() {
         return lsReqList;
     }
@@ -1713,6 +1740,7 @@
      *
      * @return reTxList instance
      */
+    @Override
     public Map getReTxList() {
         return reTxList;
     }
@@ -1722,6 +1750,7 @@
      *
      * @return pendingReTxList instance
      */
+    @Override
     public Map<String, OspfLsa> getPendingReTxList() {
         return pendingReTxList;
     }
diff --git a/protocols/ospf/ctl/src/main/java/org/onosproject/ospf/controller/lsdb/LsdbAgeImpl.java b/protocols/ospf/ctl/src/main/java/org/onosproject/ospf/controller/lsdb/LsdbAgeImpl.java
index 636e75c..794d623 100644
--- a/protocols/ospf/ctl/src/main/java/org/onosproject/ospf/controller/lsdb/LsdbAgeImpl.java
+++ b/protocols/ospf/ctl/src/main/java/org/onosproject/ospf/controller/lsdb/LsdbAgeImpl.java
@@ -80,7 +80,7 @@
         return Objects.equal(ageBins, that.ageBins) &&
                 Objects.equal(ageCounter, that.ageCounter) &&
                 Objects.equal(ageCounterRollOver, that.ageCounterRollOver) &&
-                Objects.equal(lsaQueue, lsaQueue);
+                Objects.equal(lsaQueue, that.lsaQueue);
     }
 
     @Override
@@ -94,6 +94,7 @@
      * @param binKey key to store in bin
      * @param lsaBin LSA bin instance
      */
+    @Override
     public void addLsaBin(Integer binKey, LsaBin lsaBin) {
         if (!ageBins.containsKey(binKey)) {
             ageBins.put(binKey, lsaBin);
@@ -106,6 +107,7 @@
      * @param binKey key
      * @return bin instance
      */
+    @Override
     public LsaBin getLsaBin(Integer binKey) {
 
         return ageBins.get(binKey);
@@ -117,6 +119,7 @@
      * @param key     key
      * @param wrapper wrapper instance
      */
+    @Override
     public void addLsaToMaxAgeBin(String key, LsaWrapper wrapper) {
         maxAgeBin.addOspfLsa(key, wrapper);
     }
@@ -126,6 +129,7 @@
      *
      * @param lsaWrapper wrapper instance
      */
+    @Override
     public void removeLsaFromBin(LsaWrapper lsaWrapper) {
         if (ageBins.containsKey(lsaWrapper.binNumber())) {
             LsaBin lsaBin = ageBins.get(lsaWrapper.binNumber());
@@ -137,6 +141,7 @@
     /**
      * Starts the aging timer and queue consumer.
      */
+    @Override
     public void startDbAging() {
         startDbAgeTimer();
         queueConsumer = new LsaQueueConsumer(lsaQueue, channel, ospfArea);
@@ -147,6 +152,7 @@
     /**
      * Gets called every 1 second as part of the timer.
      */
+    @Override
     public void ageLsaAndFlood() {
         //every 5 mins checksum validation
         checkAges();
@@ -167,6 +173,7 @@
     /**
      * If the LSA have completed the MaxAge - they are moved called stop aging and flooded.
      */
+    @Override
     public void maxAgeLsa() {
         if (ageCounter == 0) {
             return;
@@ -178,7 +185,7 @@
         }
         Map lsaBinMap = lsaBin.listOfLsa();
         for (Object key : lsaBinMap.keySet()) {
-            LsaWrapper lsa = (LsaWrapper) lsaBinMap.get((String) key);
+            LsaWrapper lsa = (LsaWrapper) lsaBinMap.get(key);
             if (lsa.currentAge() == OspfParameters.MAXAGE) {
                 lsa.setLsaProcessing(OspfParameters.MAXAGELSA);
                 log.debug("Lsa picked for maxage flooding. Age Counter: {}, AgeCounterRollover: {}, " +
@@ -198,7 +205,7 @@
         //Get from maxAgeBin
         Map lsaMaxAgeBinMap = maxAgeBin.listOfLsa();
         for (Object key : lsaMaxAgeBinMap.keySet()) {
-            LsaWrapper lsa = (LsaWrapper) lsaMaxAgeBinMap.get((String) key);
+            LsaWrapper lsa = (LsaWrapper) lsaMaxAgeBinMap.get(key);
             lsa.setLsaProcessing(OspfParameters.MAXAGELSA);
             log.debug("Lsa picked for maxage flooding. Age Counter: {}, LSA Type: {}, LSA Key: {}",
                       ageCounter, lsa.lsaType(), key);
@@ -217,6 +224,7 @@
     /*
      * If the LSA is in age bin of 1800 - it's pushed into refresh list.
      */
+    @Override
     public void refreshLsa() {
         int binNumber;
         if (ageCounter < OspfParameters.LSREFRESHTIME) {
@@ -230,7 +238,7 @@
         }
         Map lsaBinMap = lsaBin.listOfLsa();
         for (Object key : lsaBinMap.keySet()) {
-            LsaWrapper lsa = (LsaWrapper) lsaBinMap.get((String) key);
+            LsaWrapper lsa = (LsaWrapper) lsaBinMap.get(key);
             try {
                 if (lsa.isSelfOriginated()) {
                     log.debug("Lsa picked for refreshLsa. binNumber: {}, LSA Type: {}, LSA Key: {}",
@@ -259,7 +267,7 @@
             }
             Map lsaBinMap = lsaBin.listOfLsa();
             for (Object key : lsaBinMap.keySet()) {
-                LsaWrapper lsa = (LsaWrapper) lsaBinMap.get((String) key);
+                LsaWrapper lsa = (LsaWrapper) lsaBinMap.get(key);
                 lsa.setLsaProcessing(OspfParameters.VERIFYCHECKSUM);
                 try {
                     lsaQueue.put(lsa);
@@ -319,6 +327,7 @@
      *
      * @return ageCounter
      */
+    @Override
     public int getAgeCounter() {
         return ageCounter;
     }
@@ -328,6 +337,7 @@
      *
      * @return the age counter roll over value
      */
+    @Override
     public int getAgeCounterRollOver() {
         return ageCounterRollOver;
     }
@@ -337,6 +347,7 @@
      *
      * @return lsa bin instance
      */
+    @Override
     public LsaBin getMaxAgeBin() {
         return maxAgeBin;
     }
@@ -347,6 +358,7 @@
      * @param x Can be either age or ageCounter
      * @return bin number.
      */
+    @Override
     public int age2Bin(int x) {
         if (x <= ageCounter) {
             return (ageCounter - x);
diff --git a/protocols/ospf/protocol/src/main/java/org/onosproject/ospf/protocol/util/ChecksumCalculator.java b/protocols/ospf/protocol/src/main/java/org/onosproject/ospf/protocol/util/ChecksumCalculator.java
index ee2e712..f346f45 100644
--- a/protocols/ospf/protocol/src/main/java/org/onosproject/ospf/protocol/util/ChecksumCalculator.java
+++ b/protocols/ospf/protocol/src/main/java/org/onosproject/ospf/protocol/util/ChecksumCalculator.java
@@ -186,14 +186,12 @@
         tempLsaByte[lsaChecksumPos1] = 0;
         tempLsaByte[lsaChecksumPos2] = 0;
         byte[] byteCheckSum = {0, 0};
-        if (lsaBytes != null) {
-            for (int i = 2; i < tempLsaByte.length; i++) {
-                checksumOut[0] = checksumOut[0] + ((int) tempLsaByte[i] & 0xFF);
-                checksumOut[1] = checksumOut[1] + checksumOut[0];
-            }
-            checksumOut[0] = checksumOut[0] % 255;
-            checksumOut[1] = checksumOut[1] % 255;
+        for (int i = 2; i < tempLsaByte.length; i++) {
+            checksumOut[0] = checksumOut[0] + ((int) tempLsaByte[i] & 0xFF);
+            checksumOut[1] = checksumOut[1] + checksumOut[0];
         }
+        checksumOut[0] = checksumOut[0] % 255;
+        checksumOut[1] = checksumOut[1] % 255;
         int byte1 = (int) ((tempLsaByte.length - lsaChecksumPos1 - 1) * checksumOut[0] - checksumOut[1]) % 255;
         if (byte1 <= 0) {
             byte1 += 255;
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/TableEntryEncoder.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/TableEntryEncoder.java
index a6318de..b61ae69 100644
--- a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/TableEntryEncoder.java
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/TableEntryEncoder.java
@@ -220,8 +220,7 @@
 
         // Priority.
         // FIXME: check on P4Runtime if/what is the default priority.
-        int priority = piTableEntry.priority().orElse(0);
-        tableEntryMsgBuilder.setPriority(priority);
+        piTableEntry.priority().ifPresent(tableEntryMsgBuilder::setPriority);
 
         // Controller metadata (cookie)
         tableEntryMsgBuilder.setControllerMetadata(piTableEntry.cookie());
@@ -448,6 +447,10 @@
             case ACTION:
                 Action actionMsg = tableActionMsg.getAction();
                 return decodeActionMsg(actionMsg, browser);
+            case ACTION_PROFILE_GROUP_ID:
+                return PiActionGroupId.of(tableActionMsg.getActionProfileGroupId());
+            case ACTION_PROFILE_MEMBER_ID:
+                return PiActionGroupMemberId.of(tableActionMsg.getActionProfileMemberId());
             default:
                 throw new EncodeException(
                         format("Decoding of table action type %s not implemented", typeCase.name()));
diff --git a/protocols/p4runtime/ctl/src/test/java/org/onosproject/p4runtime/ctl/TableEntryEncoderTest.java b/protocols/p4runtime/ctl/src/test/java/org/onosproject/p4runtime/ctl/TableEntryEncoderTest.java
index b805aa9..149eae0 100644
--- a/protocols/p4runtime/ctl/src/test/java/org/onosproject/p4runtime/ctl/TableEntryEncoderTest.java
+++ b/protocols/p4runtime/ctl/src/test/java/org/onosproject/p4runtime/ctl/TableEntryEncoderTest.java
@@ -30,7 +30,9 @@
 import org.onosproject.net.pi.model.PiPipelineModel;
 import org.onosproject.net.pi.model.PiTableId;
 import org.onosproject.net.pi.runtime.PiAction;
+import org.onosproject.net.pi.runtime.PiActionGroupId;
 import org.onosproject.net.pi.runtime.PiActionParam;
+import org.onosproject.net.pi.runtime.PiExactFieldMatch;
 import org.onosproject.net.pi.runtime.PiMatchKey;
 import org.onosproject.net.pi.runtime.PiTableEntry;
 import org.onosproject.net.pi.runtime.PiTernaryFieldMatch;
@@ -57,15 +59,20 @@
 public class TableEntryEncoderTest {
     private static final String DOT = ".";
     private static final String TABLE_0 = "table0";
+    private static final String TABLE_ECMP = "ecmp";
     private static final String SET_EGRESS_PORT = "set_egress_port";
     private static final String PORT = "port";
     private static final String HDR = "hdr";
+    private static final String META = "meta";
     private static final String ETHERNET = "ethernet";
     private static final String DST_ADDR = "dstAddr";
     private static final String SRC_ADDR = "srcAddr";
     private static final String STANDARD_METADATA = "standard_metadata";
+    private static final String ECMP_METADATA = "ecmp_metadata";
     private static final String INGRESS_PORT = "ingress_port";
     private static final String ETHER_TYPE = "etherType";
+    private static final String ECMP_GROUP_ID = "ecmp_group_id";
+    private static final String ECMP_ACT_PROFILE = "ecmp_selector";
 
     private final Random rand = new Random();
     private final URL p4InfoUrl = this.getClass().getResource("/test.p4info");
@@ -83,9 +90,12 @@
     private final PiMatchFieldId ethSrcAddrFieldId = PiMatchFieldId.of(HDR + DOT + ETHERNET + DOT + SRC_ADDR);
     private final PiMatchFieldId inPortFieldId = PiMatchFieldId.of(STANDARD_METADATA + DOT + INGRESS_PORT);
     private final PiMatchFieldId ethTypeFieldId = PiMatchFieldId.of(HDR + DOT + ETHERNET + DOT + ETHER_TYPE);
+    private final PiMatchFieldId ecmpGroupFieldId =
+            PiMatchFieldId.of(META + DOT + ECMP_METADATA + DOT + ECMP_GROUP_ID);
     private final PiActionParamId portParamId = PiActionParamId.of(PORT);
     private final PiActionId outActionId = PiActionId.of(SET_EGRESS_PORT);
     private final PiTableId tableId = PiTableId.of(TABLE_0);
+    private final PiTableId ecmpTableId = PiTableId.of(TABLE_ECMP);
 
     private final PiTableEntry piTableEntry = PiTableEntry
             .builder()
@@ -105,6 +115,17 @@
             .withCookie(2)
             .build();
 
+    private final PiTableEntry piTableEntryWithGroupAction = PiTableEntry
+            .builder()
+            .forTable(ecmpTableId)
+            .withMatchKey(PiMatchKey.builder()
+                                  .addFieldMatch(new PiExactFieldMatch(ecmpGroupFieldId, ofOnes(1)))
+                                  .build())
+            .withAction(PiActionGroupId.of(1))
+            .withPriority(1)
+            .withCookie(2)
+            .build();
+
     public TableEntryEncoderTest() throws ImmutableByteSequence.ByteSequenceTrimException {
     }
 
@@ -126,8 +147,7 @@
     }
 
     @Test
-    public void testTableEntryEncoder()
-            throws P4InfoBrowser.NotFoundException, ImmutableByteSequence.ByteSequenceTrimException {
+    public void testTableEntryEncoder() throws P4InfoBrowser.NotFoundException {
 
         Collection<TableEntry> result = encode(Lists.newArrayList(piTableEntry), defaultPipeconf);
         assertThat(result, hasSize(1));
@@ -169,4 +189,33 @@
 
         // TODO: improve, assert other field match types (ternary, LPM)
     }
+
+    @Test
+    public void testActopProfileGroup() throws P4InfoBrowser.NotFoundException {
+        Collection<TableEntry> result = encode(Lists.newArrayList(piTableEntryWithGroupAction), defaultPipeconf);
+        assertThat(result, hasSize(1));
+
+        TableEntry tableEntryMsg = result.iterator().next();
+
+        Collection<PiTableEntry> decodedResults = decode(Lists.newArrayList(tableEntryMsg), defaultPipeconf);
+        PiTableEntry decodedPiTableEntry = decodedResults.iterator().next();
+
+        // Test equality for decoded entry.
+        new EqualsTester()
+                .addEqualityGroup(piTableEntryWithGroupAction, decodedPiTableEntry)
+                .testEquals();
+
+        // Table ID.
+        int p4InfoTableId = browser.tables().getByName(ecmpTableId.id()).getPreamble().getId();
+        int encodedTableId = tableEntryMsg.getTableId();
+        assertThat(encodedTableId, is(p4InfoTableId));
+
+        // Exact match.
+        byte[] encodedTernaryMatchValue = tableEntryMsg.getMatch(0).getExact().getValue().toByteArray();
+        assertThat(encodedTernaryMatchValue, is(new byte[]{(byte) 0xff}));
+
+        // Action profile group id
+        int actionProfileGroupId = tableEntryMsg.getAction().getActionProfileGroupId();
+        assertThat(actionProfileGroupId, is(1));
+    }
 }
diff --git a/protocols/pcep/pcepio/src/main/java/org/onosproject/pcepio/protocol/ver1/PcepLabelUpdateVer1.java b/protocols/pcep/pcepio/src/main/java/org/onosproject/pcepio/protocol/ver1/PcepLabelUpdateVer1.java
index 8603146..a6aa667 100644
--- a/protocols/pcep/pcepio/src/main/java/org/onosproject/pcepio/protocol/ver1/PcepLabelUpdateVer1.java
+++ b/protocols/pcep/pcepio/src/main/java/org/onosproject/pcepio/protocol/ver1/PcepLabelUpdateVer1.java
@@ -118,10 +118,7 @@
             if (isLabelMapSet) {
                 return new PcepLabelUpdateVer1(labelMap);
             }
-            if (!isLabelDownloadSet && !isLabelMapSet) {
-                throw new PcepParseException(
-                        "Label Download or Label Map is not set while building PcepLabelUpdate Message");
-            }
+
             return new PcepLabelUpdateVer1();
         }
 
diff --git a/protocols/pcep/pcepio/src/main/java/org/onosproject/pcepio/types/IPv6InterfaceAddressSubTlv.java b/protocols/pcep/pcepio/src/main/java/org/onosproject/pcepio/types/IPv6InterfaceAddressSubTlv.java
index 934c786..d318ca3 100644
--- a/protocols/pcep/pcepio/src/main/java/org/onosproject/pcepio/types/IPv6InterfaceAddressSubTlv.java
+++ b/protocols/pcep/pcepio/src/main/java/org/onosproject/pcepio/types/IPv6InterfaceAddressSubTlv.java
@@ -80,7 +80,7 @@
         boolean bFoundNoMask = true;
         //value starts from 3rd byte.
         for (int i = 2; i < 20; ++i) {
-            if (0xFF != raw[i]) {
+            if ((byte) 0xFF != raw[i]) {
                 bFoundNoMask = false;
             }
         }
diff --git a/protocols/pcep/pcepio/src/main/java/org/onosproject/pcepio/types/IPv6NeighborAddressSubTlv.java b/protocols/pcep/pcepio/src/main/java/org/onosproject/pcepio/types/IPv6NeighborAddressSubTlv.java
index 6eedfa5..7b2f5e5 100644
--- a/protocols/pcep/pcepio/src/main/java/org/onosproject/pcepio/types/IPv6NeighborAddressSubTlv.java
+++ b/protocols/pcep/pcepio/src/main/java/org/onosproject/pcepio/types/IPv6NeighborAddressSubTlv.java
@@ -78,7 +78,7 @@
         boolean bFoundNoMask = true;
         //value starts from 3rd byte.
         for (int i = 2; i < 20; ++i) {
-            if (0xFF != raw[i]) {
+            if ((byte) 0xFF != raw[i]) {
                 bFoundNoMask = false;
             }
         }
diff --git a/protocols/pcep/pcepio/src/main/java/org/onosproject/pcepio/types/IPv6RouterIdofLocalNodeSubTlv.java b/protocols/pcep/pcepio/src/main/java/org/onosproject/pcepio/types/IPv6RouterIdofLocalNodeSubTlv.java
index b269803..186f56d 100644
--- a/protocols/pcep/pcepio/src/main/java/org/onosproject/pcepio/types/IPv6RouterIdofLocalNodeSubTlv.java
+++ b/protocols/pcep/pcepio/src/main/java/org/onosproject/pcepio/types/IPv6RouterIdofLocalNodeSubTlv.java
@@ -78,7 +78,7 @@
         boolean bFoundNoMask = true;
         //value starts from 3rd byte.
         for (int i = 2; i < 20; ++i) {
-            if (0xFF != raw[i]) {
+            if ((byte) 0xFF != raw[i]) {
                 bFoundNoMask = false;
             }
         }
diff --git a/protocols/pcep/pcepio/src/main/java/org/onosproject/pcepio/types/IPv6RouterIdofRemoteNodeSubTlv.java b/protocols/pcep/pcepio/src/main/java/org/onosproject/pcepio/types/IPv6RouterIdofRemoteNodeSubTlv.java
index 1512aa2..021da71 100644
--- a/protocols/pcep/pcepio/src/main/java/org/onosproject/pcepio/types/IPv6RouterIdofRemoteNodeSubTlv.java
+++ b/protocols/pcep/pcepio/src/main/java/org/onosproject/pcepio/types/IPv6RouterIdofRemoteNodeSubTlv.java
@@ -79,7 +79,7 @@
         boolean bFoundNoMask = true;
         //value starts from 3rd byte.
         for (int i = 2; i < 20; ++i) {
-            if (0xFF != raw[i]) {
+            if ((byte) 0xFF != raw[i]) {
                 bFoundNoMask = false;
             }
         }
diff --git a/protocols/pcep/pcepio/src/main/java/org/onosproject/pcepio/types/IPv6SubObject.java b/protocols/pcep/pcepio/src/main/java/org/onosproject/pcepio/types/IPv6SubObject.java
index e570255..808b1e8 100644
--- a/protocols/pcep/pcepio/src/main/java/org/onosproject/pcepio/types/IPv6SubObject.java
+++ b/protocols/pcep/pcepio/src/main/java/org/onosproject/pcepio/types/IPv6SubObject.java
@@ -130,7 +130,7 @@
         boolean bFoundNoMask = true;
         //value starts from 3rd byte.
         for (int i = 2; i < 20; ++i) {
-            if (0xFF != raw[i]) {
+            if ((byte) 0xFF != raw[i]) {
                 bFoundNoMask = false;
             }
         }
diff --git a/protocols/pcep/pcepio/src/main/java/org/onosproject/pcepio/types/NexthopIPv6addressTlv.java b/protocols/pcep/pcepio/src/main/java/org/onosproject/pcepio/types/NexthopIPv6addressTlv.java
index fdcf9e7..2c031ab 100644
--- a/protocols/pcep/pcepio/src/main/java/org/onosproject/pcepio/types/NexthopIPv6addressTlv.java
+++ b/protocols/pcep/pcepio/src/main/java/org/onosproject/pcepio/types/NexthopIPv6addressTlv.java
@@ -99,7 +99,7 @@
         boolean bFoundNoMask = true;
         //value starts from 3rd byte.
         for (int i = 5; i < 20; ++i) {
-            if (0xFF != raw[i]) {
+            if ((byte) 0xFF != raw[i]) {
                 bFoundNoMask = false;
             }
         }
diff --git a/protocols/pcep/server/ctl/src/main/java/org/onosproject/pcep/server/impl/BasicPceccHandler.java b/protocols/pcep/server/ctl/src/main/java/org/onosproject/pcep/server/impl/BasicPceccHandler.java
index c547b35..7376c14 100644
--- a/protocols/pcep/server/ctl/src/main/java/org/onosproject/pcep/server/impl/BasicPceccHandler.java
+++ b/protocols/pcep/server/ctl/src/main/java/org/onosproject/pcep/server/impl/BasicPceccHandler.java
@@ -113,7 +113,7 @@
      *
      * @return this class single instance
      */
-    public static BasicPceccHandler getInstance() {
+    public static synchronized BasicPceccHandler getInstance() {
         if (crHandlerInstance == null) {
             crHandlerInstance = new BasicPceccHandler();
         }
diff --git a/protocols/pcep/server/ctl/src/main/java/org/onosproject/pcep/server/impl/PceccSrTeBeHandler.java b/protocols/pcep/server/ctl/src/main/java/org/onosproject/pcep/server/impl/PceccSrTeBeHandler.java
index 8d50149..682f242 100644
--- a/protocols/pcep/server/ctl/src/main/java/org/onosproject/pcep/server/impl/PceccSrTeBeHandler.java
+++ b/protocols/pcep/server/ctl/src/main/java/org/onosproject/pcep/server/impl/PceccSrTeBeHandler.java
@@ -96,7 +96,7 @@
      *
      * @return this class single instance
      */
-    public static PceccSrTeBeHandler getInstance() {
+    public static synchronized PceccSrTeBeHandler getInstance() {
         if (srTeHandlerInstance == null) {
             srTeHandlerInstance = new PceccSrTeBeHandler();
         }
diff --git a/protocols/pcep/server/ctl/src/main/java/org/onosproject/pcep/server/impl/PcepChannelHandler.java b/protocols/pcep/server/ctl/src/main/java/org/onosproject/pcep/server/impl/PcepChannelHandler.java
index dce5fa0..596a633 100644
--- a/protocols/pcep/server/ctl/src/main/java/org/onosproject/pcep/server/impl/PcepChannelHandler.java
+++ b/protocols/pcep/server/ctl/src/main/java/org/onosproject/pcep/server/impl/PcepChannelHandler.java
@@ -485,9 +485,8 @@
      * @throws PcepParseException while building pcep error message
      */
     public void processUnknownMsg() throws PcepParseException {
-        Date now = null;
+        Date now = new Date();
         if (pcepPacketStats.wrongPacketCount() == 0) {
-            now = new Date();
             pcepPacketStats.setTime(now.getTime());
             pcepPacketStats.addWrongPacket();
             sendErrMsgForInvalidMsg();
diff --git a/protocols/pcep/server/ctl/src/main/java/org/onosproject/pcep/server/impl/PcepClientControllerImpl.java b/protocols/pcep/server/ctl/src/main/java/org/onosproject/pcep/server/impl/PcepClientControllerImpl.java
index 2ddc819..1bb5610 100644
--- a/protocols/pcep/server/ctl/src/main/java/org/onosproject/pcep/server/impl/PcepClientControllerImpl.java
+++ b/protocols/pcep/server/ctl/src/main/java/org/onosproject/pcep/server/impl/PcepClientControllerImpl.java
@@ -1005,6 +1005,10 @@
                     }
                 }
 
+                if (ipv4LspIdenTlv == null) {
+                    return false;
+                }
+
                 LspKey lspKeyOfRpt = new LspKey(lspObj.getPlspId(), ipv4LspIdenTlv.getLspId());
                 tunnel = preSyncLspDbByKey.get(lspKeyOfRpt);
                 // PCE tunnel is matched with PCRpt LSP. Now delete it from the preSyncLspDb list as the residual
@@ -1059,7 +1063,8 @@
                 // State different for PCC sent LSP and PCE known LSP, send PCUpd msg.
                 State tunnelState = PcepLspStatus
                         .getTunnelStatusFromLspStatus(PcepLspStatus.values()[lspObj.getOFlag()]);
-                if (tunnelState != tunnel.state()) {
+
+                if (tunnel != null && tunnelState != tunnel.state()) {
                     for (PcepEventListener l : pcepEventListener) {
                         l.handleEndOfSyncAction(tunnel, SEND_UPDATE);
                     }
diff --git a/protocols/pcep/server/ctl/src/test/java/org/onosproject/pcelabelstore/util/TestEventuallyConsistentMap.java b/protocols/pcep/server/ctl/src/test/java/org/onosproject/pcelabelstore/util/TestEventuallyConsistentMap.java
index 6c34554..9184352 100644
--- a/protocols/pcep/server/ctl/src/test/java/org/onosproject/pcelabelstore/util/TestEventuallyConsistentMap.java
+++ b/protocols/pcep/server/ctl/src/test/java/org/onosproject/pcelabelstore/util/TestEventuallyConsistentMap.java
@@ -90,6 +90,7 @@
         return map.get(key);
     }
 
+    @SuppressWarnings("ReturnValueIgnored")
     @Override
     public void put(K key, V value) {
         map.put(key, value);
diff --git a/protocols/restconf/server/rpp/BUCK b/protocols/restconf/server/rpp/BUCK
index 8f7bb52..faeeb6f 100644
--- a/protocols/restconf/server/rpp/BUCK
+++ b/protocols/restconf/server/rpp/BUCK
@@ -9,7 +9,14 @@
     '//apps/restconf/api:onos-apps-restconf-api',
 ]
 
+TEST_DEPS = [
+    '//lib:TEST_REST',
+    '//utils/osgi:onlab-osgi-tests',
+    '//web/api:onos-rest-tests',
+]
+
 osgi_jar_with_tests (
     deps = COMPILE_DEPS,
+    test_deps = TEST_DEPS,
     web_context = '/onos/restconf',
 )
diff --git a/protocols/restconf/server/rpp/pom.xml b/protocols/restconf/server/rpp/pom.xml
index 6f88aaf..07e2f8b 100644
--- a/protocols/restconf/server/rpp/pom.xml
+++ b/protocols/restconf/server/rpp/pom.xml
@@ -95,6 +95,18 @@
             <version>2.25.1</version>
         </dependency>
         <dependency>
+            <groupId>org.glassfish.jersey.test-framework</groupId>
+            <artifactId>jersey-test-framework-core</artifactId>
+            <version>${jersey.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-jetty</artifactId>
+            <version>${jersey.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.onosproject</groupId>
             <artifactId>onlab-misc</artifactId>
             <version>${project.version}</version>
@@ -109,6 +121,19 @@
           <artifactId>servlet-api</artifactId>
           <version>2.5</version>
        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-rest</artifactId>
+            <version>${project.version}</version>
+            <classifier>tests</classifier>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-osgi</artifactId>
+            <classifier>tests</classifier>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/protocols/restconf/server/rpp/src/main/java/org/onosproject/protocol/restconf/server/rpp/RestconfWebResource.java b/protocols/restconf/server/rpp/src/main/java/org/onosproject/protocol/restconf/server/rpp/RestconfWebResource.java
index ba11421..36eb3a2 100644
--- a/protocols/restconf/server/rpp/src/main/java/org/onosproject/protocol/restconf/server/rpp/RestconfWebResource.java
+++ b/protocols/restconf/server/rpp/src/main/java/org/onosproject/protocol/restconf/server/rpp/RestconfWebResource.java
@@ -20,6 +20,7 @@
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.glassfish.jersey.server.ChunkedOutput;
 import org.onosproject.rest.AbstractWebResource;
+import org.onosproject.restconf.api.RestconfError;
 import org.onosproject.restconf.api.RestconfException;
 import org.onosproject.restconf.api.RestconfRpcOutput;
 import org.onosproject.restconf.api.RestconfService;
@@ -42,6 +43,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URI;
+import java.util.Arrays;
 import java.util.concurrent.CompletableFuture;
 
 import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
@@ -68,22 +70,19 @@
     @Context
     UriInfo uriInfo;
 
-    private static final String NOT_EXIST = "Requested data resource does not exist";
-
     private final RestconfService service = get(RestconfService.class);
     private final Logger log = getLogger(getClass());
 
     /**
      * Handles a RESTCONF GET operation against a target data resource. If the
      * operation is successful, the JSON presentation of the resource plus HTTP
-     * status code "200 OK" is returned. Otherwise, HTTP error status code
-     * "400 Bad Request" is returned.
+     * status code "200 OK" is returned. If it is not found then "404 Not Found"
+     * is returned. On internal error "500 Internal Server Error" is returned.
      *
      * @param uriString URI of the data resource.
-     * @return HTTP response
+     * @return HTTP response - 200, 404 or 500
      */
     @GET
-    @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
     @Path("data/{identifier : .+}")
     public Response handleGetRequest(@PathParam("identifier") String uriString) {
@@ -94,13 +93,27 @@
         try {
             ObjectNode node = service.runGetOperationOnDataResource(uri);
             if (node == null) {
-                return Response.status(NOT_FOUND).entity(NOT_EXIST).build();
+                RestconfError error =
+                        RestconfError.builder(RestconfError.ErrorType.PROTOCOL,
+                                RestconfError.ErrorTag.INVALID_VALUE)
+                        .errorMessage("Resource not found")
+                        .errorPath(uriString)
+                        .errorAppTag("handleGetRequest")
+                        .build();
+                return Response.status(NOT_FOUND)
+                        .entity(RestconfError.wrapErrorAsJson(Arrays.asList(error))).build();
             }
             return ok(node).build();
         } catch (RestconfException e) {
             log.error("ERROR: handleGetRequest: {}", e.getMessage());
             log.debug("Exception in handleGetRequest:", e);
-            return e.getResponse();
+            return Response.status(e.getResponse().getStatus()).entity(e.toRestconfErrorJson()).build();
+        } catch (Exception e) {
+            RestconfError error = RestconfError
+                    .builder(RestconfError.ErrorType.APPLICATION, RestconfError.ErrorTag.OPERATION_FAILED)
+                    .errorMessage(e.getMessage()).errorAppTag("handlePostRequest").build();
+            return Response.status(INTERNAL_SERVER_ERROR)
+                    .entity(RestconfError.wrapErrorAsJson(Arrays.asList(error))).build();
         }
     }
 
@@ -186,14 +199,23 @@
             return Response.created(uriInfo.getRequestUri()).build();
         } catch (JsonProcessingException e) {
             log.error("ERROR: handlePostRequest ", e);
-            return Response.status(BAD_REQUEST).build();
+            RestconfError error = RestconfError
+                    .builder(RestconfError.ErrorType.APPLICATION, RestconfError.ErrorTag.MALFORMED_MESSAGE)
+                    .errorMessage(e.getMessage()).errorAppTag("handlePostRequest").build();
+            return Response.status(BAD_REQUEST)
+                    .entity(RestconfError.wrapErrorAsJson(Arrays.asList(error))).build();
         } catch (RestconfException e) {
             log.error("ERROR: handlePostRequest: {}", e.getMessage());
             log.debug("Exception in handlePostRequest:", e);
-            return e.getResponse();
+            return Response.status(e.getResponse().getStatus())
+                    .entity(e.toRestconfErrorJson()).build();
         } catch (IOException ex) {
             log.error("ERROR: handlePostRequest ", ex);
-            return Response.status(INTERNAL_SERVER_ERROR).build();
+            RestconfError error = RestconfError
+                    .builder(RestconfError.ErrorType.APPLICATION, RestconfError.ErrorTag.OPERATION_FAILED)
+                    .errorMessage(ex.getMessage()).errorAppTag("handlePostRequest").build();
+            return Response.status(INTERNAL_SERVER_ERROR)
+                    .entity(RestconfError.wrapErrorAsJson(Arrays.asList(error))).build();
         }
     }
 
@@ -230,14 +252,23 @@
             return ok(node).build();
         } catch (JsonProcessingException e) {
             log.error("ERROR:  handleRpcRequest", e);
-            return Response.status(BAD_REQUEST).build();
+            RestconfError error = RestconfError
+                    .builder(RestconfError.ErrorType.APPLICATION, RestconfError.ErrorTag.MALFORMED_MESSAGE)
+                    .errorMessage(e.getMessage()).errorAppTag("handleRpcRequest").build();
+            return Response.status(BAD_REQUEST)
+                    .entity(RestconfError.wrapErrorAsJson(Arrays.asList(error))).build();
         } catch (RestconfException e) {
             log.error("ERROR: handleRpcRequest: {}", e.getMessage());
             log.debug("Exception in handleRpcRequest:", e);
-            return e.getResponse();
+            return Response.status(e.getResponse().getStatus())
+                    .entity(e.toRestconfErrorJson()).build();
         } catch (Exception e) {
             log.error("ERROR: handleRpcRequest ", e);
-            return Response.status(INTERNAL_SERVER_ERROR).build();
+            RestconfError error = RestconfError
+                    .builder(RestconfError.ErrorType.APPLICATION, RestconfError.ErrorTag.OPERATION_FAILED)
+                    .errorMessage(e.getMessage()).errorAppTag("handleRpcRequest").build();
+            return Response.status(INTERNAL_SERVER_ERROR)
+                    .entity(RestconfError.wrapErrorAsJson(Arrays.asList(error))).build();
         }
     }
 
@@ -272,14 +303,23 @@
             return Response.created(uriInfo.getRequestUri()).build();
         } catch (JsonProcessingException e) {
             log.error("ERROR: handlePutRequest ", e);
-            return Response.status(BAD_REQUEST).build();
+            RestconfError error = RestconfError
+                    .builder(RestconfError.ErrorType.APPLICATION, RestconfError.ErrorTag.MALFORMED_MESSAGE)
+                    .errorMessage(e.getMessage()).errorAppTag("handlePutRequest").build();
+            return Response.status(BAD_REQUEST)
+                    .entity(RestconfError.wrapErrorAsJson(Arrays.asList(error))).build();
         } catch (RestconfException e) {
             log.error("ERROR: handlePutRequest: {}", e.getMessage());
             log.debug("Exception in handlePutRequest:", e);
-            return e.getResponse();
+            return Response.status(e.getResponse().getStatus())
+                    .entity(e.toRestconfErrorJson()).build();
         } catch (IOException ex) {
             log.error("ERROR: handlePutRequest ", ex);
-            return Response.status(INTERNAL_SERVER_ERROR).build();
+            RestconfError error = RestconfError
+                    .builder(RestconfError.ErrorType.APPLICATION, RestconfError.ErrorTag.OPERATION_FAILED)
+                    .errorMessage(ex.getMessage()).errorAppTag("handlePutRequest").build();
+            return Response.status(INTERNAL_SERVER_ERROR)
+                    .entity(RestconfError.wrapErrorAsJson(Arrays.asList(error))).build();
         }
     }
 
@@ -294,7 +334,6 @@
      * @return HTTP response
      */
     @DELETE
-    @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
     @Path("data/{identifier : .+}")
     public Response handleDeleteRequest(@PathParam("identifier") String uriString) {
@@ -308,7 +347,8 @@
         } catch (RestconfException e) {
             log.error("ERROR: handleDeleteRequest: {}", e.getMessage());
             log.debug("Exception in handleDeleteRequest:", e);
-            return e.getResponse();
+            return Response.status(e.getResponse().getStatus())
+                    .entity(e.toRestconfErrorJson()).build();
         }
     }
 
@@ -340,14 +380,23 @@
             return Response.ok().build();
         } catch (JsonProcessingException e) {
             log.error("ERROR: handlePatchRequest ", e);
-            return Response.status(BAD_REQUEST).build();
+            RestconfError error = RestconfError
+                    .builder(RestconfError.ErrorType.APPLICATION, RestconfError.ErrorTag.MALFORMED_MESSAGE)
+                    .errorMessage(e.getMessage()).errorAppTag("handlePatchRequest").build();
+            return Response.status(BAD_REQUEST)
+                    .entity(RestconfError.wrapErrorAsJson(Arrays.asList(error))).build();
         } catch (RestconfException e) {
             log.error("ERROR: handlePatchRequest: {}", e.getMessage());
             log.debug("Exception in handlePatchRequest:", e);
-            return e.getResponse();
+            return Response.status(e.getResponse().getStatus())
+                    .entity(e.toRestconfErrorJson()).build();
         } catch (IOException ex) {
             log.error("ERROR: handlePatchRequest ", ex);
-            return Response.status(INTERNAL_SERVER_ERROR).build();
+            RestconfError error = RestconfError
+                    .builder(RestconfError.ErrorType.APPLICATION, RestconfError.ErrorTag.OPERATION_FAILED)
+                    .errorMessage(ex.getMessage()).errorAppTag("handlePatchRequest").build();
+            return Response.status(INTERNAL_SERVER_ERROR)
+                    .entity(RestconfError.wrapErrorAsJson(Arrays.asList(error))).build();
         }
     }
 
diff --git a/protocols/restconf/server/rpp/src/test/java/org/onosproject/protocol/restconf/server/rpp/RestconfWebResourceTest.java b/protocols/restconf/server/rpp/src/test/java/org/onosproject/protocol/restconf/server/rpp/RestconfWebResourceTest.java
new file mode 100644
index 0000000..603bbf6
--- /dev/null
+++ b/protocols/restconf/server/rpp/src/test/java/org/onosproject/protocol/restconf/server/rpp/RestconfWebResourceTest.java
@@ -0,0 +1,222 @@
+/*
+ * 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.protocol.restconf.server.rpp;
+
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.easymock.EasyMock;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onosproject.rest.resources.ResourceTest;
+import org.onosproject.restconf.api.RestconfError;
+import org.onosproject.restconf.api.RestconfException;
+import org.onosproject.restconf.api.RestconfService;
+
+import javax.ws.rs.InternalServerErrorException;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+import java.io.ByteArrayInputStream;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.util.Optional;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static javax.ws.rs.core.Response.Status.CONFLICT;
+import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
+import static junit.framework.TestCase.assertTrue;
+import static junit.framework.TestCase.fail;
+import static org.easymock.EasyMock.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * Test the RestconfWebResource.
+ */
+public class RestconfWebResourceTest extends ResourceTest {
+
+    public static final String DATA_IETF_SYSTEM_SYSTEM = "data/ietf-system:system";
+
+    private static final Pattern RESTCONF_ERROR_REGEXP =
+            Pattern.compile("(\\{\"ietf-restconf:errors\":\\[)\\R?"
+                    + "((\\{\"error\":)\\R?"
+                    + "(\\{\"error-type\":\")((protocol)|(transport)|(rpc)|(application))(\",)\\R?"
+                    + "(\"error-tag\":\")[a-z\\-]*(\",)\\R?"
+                    + "((\"error-app-tag\":\").*(\",))?\\R?"
+                    + "((\"error-path\":\").*(\",))?\\R?"
+                    + "((\"error-message\":\").*(\"))?(\\}\\},?))*(\\]\\})", Pattern.DOTALL);
+
+    private RestconfService restconfService = createMock(RestconfService.class);
+
+    public RestconfWebResourceTest() {
+        super(ResourceConfig.forApplicationClass(RestconfProtocolProxy.class));
+    }
+
+    @Before
+    public void setup() {
+        ServiceDirectory testDirectory = new TestServiceDirectory()
+                .add(RestconfService.class, restconfService);
+        setServiceDirectory(testDirectory);
+    }
+
+    /**
+     * Test handleGetRequest when an Json object is returned.
+     */
+    @Test
+    public void testHandleGetRequest() {
+        ObjectMapper mapper = new ObjectMapper();
+        ObjectNode node = mapper.createObjectNode();
+        expect(restconfService
+                .runGetOperationOnDataResource(URI.create(getBaseUri() + DATA_IETF_SYSTEM_SYSTEM)))
+                .andReturn(node).anyTimes();
+        replay(restconfService);
+
+        WebTarget wt = target();
+        String response = wt.path("/" + DATA_IETF_SYSTEM_SYSTEM).request().get(String.class);
+        assertNotNull(response);
+    }
+
+    /**
+     * Test handleGetRequest when nothing is returned.
+     */
+    @Test
+    public void testHandleGetRequestNotFound() {
+        expect(restconfService
+                .runGetOperationOnDataResource(URI.create(getBaseUri() + DATA_IETF_SYSTEM_SYSTEM)))
+                .andReturn(null).anyTimes();
+        replay(restconfService);
+
+        WebTarget wt = target();
+        try {
+            String response = wt.path("/" + DATA_IETF_SYSTEM_SYSTEM).request().get(String.class);
+            fail("Expecting fail as response is none");
+        } catch (NotFoundException e) {
+            assertNotNull(e.getResponse());
+            assertRestconfErrorJson(e.getResponse());
+        }
+    }
+
+    /**
+     * Test handleGetRequest when an RestconfException is thrown.
+     */
+    @Test
+    public void testHandleGetRequestRestconfException() {
+        expect(restconfService
+                .runGetOperationOnDataResource(URI.create(getBaseUri() + DATA_IETF_SYSTEM_SYSTEM)))
+                .andThrow(new RestconfException("Suitable error message",
+                        RestconfError.ErrorTag.OPERATION_FAILED, INTERNAL_SERVER_ERROR,
+                        Optional.of("/" + DATA_IETF_SYSTEM_SYSTEM),
+                        Optional.of("More info about the error")))
+                .anyTimes();
+        replay(restconfService);
+
+        WebTarget wt = target();
+        try {
+            String response = wt.path("/" + DATA_IETF_SYSTEM_SYSTEM).request().get(String.class);
+            fail("Expecting fail as response is RestconfException");
+        } catch (InternalServerErrorException e) {
+            assertNotNull(e.getResponse());
+            assertRestconfErrorJson(e.getResponse());
+        }
+    }
+
+    /**
+     * Test handleGetRequest when an Exception is thrown.
+     */
+    @Test
+    public void testHandleGetRequestIoException() {
+        expect(restconfService
+                .runGetOperationOnDataResource(URI.create(getBaseUri() + DATA_IETF_SYSTEM_SYSTEM)))
+                .andThrow(new IllegalArgumentException("A test exception"))
+                .anyTimes();
+        replay(restconfService);
+
+        WebTarget wt = target();
+        try {
+            String response = wt.path("/" + DATA_IETF_SYSTEM_SYSTEM).request().get(String.class);
+            fail("Expecting fail as response is IllegalArgumentException");
+        } catch (InternalServerErrorException e) {
+            assertNotNull(e.getResponse());
+            assertRestconfErrorJson(e.getResponse());
+        }
+    }
+
+    /**
+     * Test handlePostRequest with no exception.
+     */
+    @Test
+    public void testHandlePostRequest() {
+        ObjectMapper mapper = new ObjectMapper();
+        ObjectNode ietfSystemSubNode = mapper.createObjectNode();
+        ietfSystemSubNode.put("contact", "Open Networking Foundation");
+        ietfSystemSubNode.put("hostname", "host1");
+        ietfSystemSubNode.put("location", "The moon");
+
+        ObjectNode ietfSystemNode = mapper.createObjectNode();
+        ietfSystemNode.put("ietf-system:system", ietfSystemSubNode);
+
+        WebTarget wt = target();
+        Response response = wt.path("/" + DATA_IETF_SYSTEM_SYSTEM)
+                .request()
+                .post(Entity.json(ietfSystemNode.toString()));
+        assertEquals(201, response.getStatus());
+    }
+
+    /**
+     * Test handlePostRequest with 'already exists' exception.
+     */
+    @Test
+    public void testHandlePostRequestAlreadyExists() {
+        ObjectMapper mapper = new ObjectMapper();
+        ObjectNode ietfSystemSubNode = mapper.createObjectNode();
+        ietfSystemSubNode.put("contact", "Open Networking Foundation");
+        ietfSystemSubNode.put("hostname", "host1");
+        ietfSystemSubNode.put("location", "The moon");
+
+        ObjectNode ietfSystemNode = mapper.createObjectNode();
+        ietfSystemNode.put("ietf-system:system", ietfSystemSubNode);
+
+        restconfService.runPostOperationOnDataResource(
+                EasyMock.<URI>anyObject(), EasyMock.<ObjectNode>anyObject());
+        expectLastCall().andThrow(new RestconfException("Requested node already present", null,
+                RestconfError.ErrorTag.DATA_EXISTS, CONFLICT,
+                Optional.of("/" + DATA_IETF_SYSTEM_SYSTEM)));
+        replay(restconfService);
+
+        WebTarget wt = target();
+        Response response = wt.path("/" + DATA_IETF_SYSTEM_SYSTEM)
+                .request()
+                .post(Entity.json(ietfSystemNode.toString()));
+        assertEquals(409, response.getStatus());
+    }
+
+    private static void assertRestconfErrorJson(Response errorResponse) {
+        ByteArrayInputStream in = (ByteArrayInputStream) errorResponse.getEntity();
+        int n = in.available();
+        byte[] bytes = new byte[n];
+        in.read(bytes, 0, n);
+
+        Matcher m = RESTCONF_ERROR_REGEXP.matcher(new String(bytes, StandardCharsets.UTF_8));
+        assertTrue(m.matches());
+    }
+}
diff --git a/protocols/snmp/ctl/src/main/java/org/onosproject/snmp/ctl/DefaultSnmpDevice.java b/protocols/snmp/ctl/src/main/java/org/onosproject/snmp/ctl/DefaultSnmpDevice.java
index 5e1f867..b15277c 100644
--- a/protocols/snmp/ctl/src/main/java/org/onosproject/snmp/ctl/DefaultSnmpDevice.java
+++ b/protocols/snmp/ctl/src/main/java/org/onosproject/snmp/ctl/DefaultSnmpDevice.java
@@ -50,7 +50,7 @@
     public DefaultSnmpDevice(String snmpHost, int snmpPort, String username, String community) {
 
         this.snmpHost = checkNotNull(snmpHost, "SNMP Device IP cannot be null");
-        this.snmpPort = checkNotNull(snmpPort, "SNMP Device port cannot be null");
+        this.snmpPort = snmpPort;
         this.username = username;
         this.community = community;
         this.deviceId = createDeviceId();
diff --git a/providers/general/device/src/main/java/org/onosproject/provider/general/device/impl/GeneralDeviceProvider.java b/providers/general/device/src/main/java/org/onosproject/provider/general/device/impl/GeneralDeviceProvider.java
index f309497..c439094 100644
--- a/providers/general/device/src/main/java/org/onosproject/provider/general/device/impl/GeneralDeviceProvider.java
+++ b/providers/general/device/src/main/java/org/onosproject/provider/general/device/impl/GeneralDeviceProvider.java
@@ -441,15 +441,17 @@
                     //Empty list of ports
                     List<PortDescription> ports = new ArrayList<>();
 
-                    if (driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
-                        DeviceDescriptionDiscovery deviceDiscovery = driver
-                                .createBehaviour(driverData, DeviceDescriptionDiscovery.class);
-
+                    DeviceDescriptionDiscovery deviceDiscovery = getBehaviour(driver,
+                            DeviceDescriptionDiscovery.class, driverData);
+                    if (deviceDiscovery != null) {
                         DeviceDescription newdescription = deviceDiscovery.discoverDeviceDetails();
                         if (newdescription != null) {
                             description = newdescription;
                         }
                         ports = deviceDiscovery.discoverPortDetails();
+                    } else {
+                        log.info("No Device Description Discovery for device {}, no update for " +
+                                "description or ports.", deviceId);
                     }
 
                     if (!handlePipeconf(deviceId, driver, driverData, true)) {
diff --git a/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java b/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java
index eadb4cf..0633d12 100644
--- a/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java
+++ b/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java
@@ -418,6 +418,7 @@
             // IPv6: Use Neighbor Discovery
             //TODO need to implement ndp probe
             log.info("Triggering probe on device {} ", host);
+            return;
         }
 
         TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(host.location().port()).build();
diff --git a/providers/link/src/test/java/org/onosproject/provider/linkdiscovery/impl/LinkDiscoveryProviderTest.java b/providers/link/src/test/java/org/onosproject/provider/linkdiscovery/impl/LinkDiscoveryProviderTest.java
index a1872c8..30ddf2a 100644
--- a/providers/link/src/test/java/org/onosproject/provider/linkdiscovery/impl/LinkDiscoveryProviderTest.java
+++ b/providers/link/src/test/java/org/onosproject/provider/linkdiscovery/impl/LinkDiscoveryProviderTest.java
@@ -149,7 +149,7 @@
 
     @Test
     public void activate() throws Exception {
-        assertFalse("Provider should be registered", linkRegistry.getProviders().contains(provider));
+        assertTrue("Provider should be registered", linkRegistry.getProviders().contains(provider.id()));
         assertEquals("Device service should be registered", provider.deviceService, deviceService);
         assertEquals("Device listener should be added", 1, deviceListeners.size());
         assertNotNull("Registration expected", providerService);
@@ -177,7 +177,7 @@
     public void deactivate() throws Exception {
         provider.deactivate();
         assertEquals("Device listener should be removed", 0, deviceListeners.size());
-        assertFalse("Provider should not be registered", linkRegistry.getProviders().contains(provider));
+        assertFalse("Provider should not be registered", linkRegistry.getProviders().contains(provider.id()));
         assertTrue(provider.executor.isShutdown());
         assertNull(provider.providerService);
     }
diff --git a/providers/lisp/device/src/test/java/org/onosproject/provider/lisp/device/impl/LispDeviceProviderTest.java b/providers/lisp/device/src/test/java/org/onosproject/provider/lisp/device/impl/LispDeviceProviderTest.java
index 74a28f0..90a15fa 100644
--- a/providers/lisp/device/src/test/java/org/onosproject/provider/lisp/device/impl/LispDeviceProviderTest.java
+++ b/providers/lisp/device/src/test/java/org/onosproject/provider/lisp/device/impl/LispDeviceProviderTest.java
@@ -97,7 +97,7 @@
         provider.deactivate();
 
         assertFalse("Provider should not be registered",
-                            providerRegistry.getProviders().contains(provider));
+                            providerRegistry.getProviders().contains(provider.id()));
         assertNull("Provider service should be null",
                             provider.providerService);
         assertEquals("Controller listener should be removed", 0,
diff --git a/providers/lisp/mapping/src/test/java/org/onosproject/provider/lisp/mapping/impl/LispMappingProviderTest.java b/providers/lisp/mapping/src/test/java/org/onosproject/provider/lisp/mapping/impl/LispMappingProviderTest.java
index 17c758c..8424454 100644
--- a/providers/lisp/mapping/src/test/java/org/onosproject/provider/lisp/mapping/impl/LispMappingProviderTest.java
+++ b/providers/lisp/mapping/src/test/java/org/onosproject/provider/lisp/mapping/impl/LispMappingProviderTest.java
@@ -78,7 +78,7 @@
         provider.deactivate();
 
         assertFalse("Provider should not be registered",
-                            providerRegistry.getProviders().contains(provider));
+                            providerRegistry.getProviders().contains(provider.id()));
         assertNull("Provider service should be null",
                             provider.providerService);
         assertEquals("Controller listener should be removed", 0,
diff --git a/providers/netconf/device/src/test/java/org/onosproject/provider/netconf/device/impl/NetconfDeviceProviderTest.java b/providers/netconf/device/src/test/java/org/onosproject/provider/netconf/device/impl/NetconfDeviceProviderTest.java
index f37d41e..802c002 100644
--- a/providers/netconf/device/src/test/java/org/onosproject/provider/netconf/device/impl/NetconfDeviceProviderTest.java
+++ b/providers/netconf/device/src/test/java/org/onosproject/provider/netconf/device/impl/NetconfDeviceProviderTest.java
@@ -225,7 +225,7 @@
     public void deactivate() throws Exception {
         provider.deactivate();
         assertNull("Device listener should be removed", deviceService.listener);
-        assertFalse("Provider should not be registered", deviceRegistry.getProviders().contains(provider));
+        assertFalse("Provider should not be registered", deviceRegistry.getProviders().contains(provider.id()));
         assertTrue("Thread to connect device should be shutdown", provider.executor.isShutdown());
         assertTrue("Scheduled task to update device should be shutdown", provider.scheduledTask.isCancelled());
         assertNull("Provider service should be null", provider.providerService);
diff --git a/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java b/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java
index f3dd6e0..b489c52 100644
--- a/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java
+++ b/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java
@@ -86,6 +86,7 @@
 import org.projectfloodlight.openflow.protocol.OFObject;
 import org.projectfloodlight.openflow.protocol.OFPortConfig;
 import org.projectfloodlight.openflow.protocol.OFPortDesc;
+import org.projectfloodlight.openflow.protocol.OFPortDescProp;
 import org.projectfloodlight.openflow.protocol.OFPortDescPropEthernet;
 import org.projectfloodlight.openflow.protocol.OFPortDescPropOptical;
 import org.projectfloodlight.openflow.protocol.OFPortDescPropOpticalTransport;
@@ -156,54 +157,115 @@
     public static final String AK_MIN_FREQ_HZ = "minFrequency";
 
     /**
+     * Annotation key for minimum lambda in nm.
+     * Value is expected to be an integer.
+     */
+    public static final String AK_MIN_LMDA_NM = "minLambda";
+
+    /**
      * Annotation key for maximum frequency in Hz.
      * Value is expected be an integer.
      */
     public static final String AK_MAX_FREQ_HZ = "maxFrequency";
 
     /**
-     * Annotation key for grid in Hz.
+     * Annotation key for maximum lambda in nm.
+     * Value is expected be an integer.
+     */
+    public static final String AK_MAX_LMDA_NM = "maxLambda";
+
+    /**
+     * Annotation key for grid frequency in Hz.
      * Value is expected to be an integer.
      */
     public static final String AK_GRID_HZ = "grid";
 
     /**
+     * Annotation key for grid lambda in nm.
+     * Value is expected to be an integer.
+     */
+    public static final String AK_GRID_LMDA_NM = "gridLambda";
+
+    /**
      * Annotation key for minimum frequency in Hz.
      * Value is expected to be an integer.
      */
     public static final String AK_TX_MIN_FREQ_HZ = "txMinFrequency";
 
     /**
+     * Annotation key for minimum lambda in nm.
+     * Value is expected to be an integer.
+     */
+    public static final String AK_TX_MIN_LMDA_NM = "txMinLambda";
+
+    /**
      * Annotation key for maximum frequency in Hz.
      * Value is expected be an integer.
      */
     public static final String AK_TX_MAX_FREQ_HZ = "txMaxFrequency";
 
     /**
-     * Annotation key for grid in Hz.
+     * Annotation key for maximum lambda in nm.
+     * Value is expected be an integer.
+     */
+    public static final String AK_TX_MAX_LMDA_NM = "txMaxLambda";
+
+    /**
+     * Annotation key for grid frequency in Hz.
      * Value is expected to be an integer.
      */
     public static final String AK_TX_GRID_HZ = "txGrid";
 
     /**
+     * Annotation key for grid lambda in nm.
+     * Value is expected to be an integer.
+     */
+    public static final String AK_TX_GRID_LMDA_NM = "txGridLambda";
+
+    /**
      * Annotation key for minimum frequency in Hz.
      * Value is expected to be an integer.
      */
     public static final String AK_RX_MIN_FREQ_HZ = "rxMinFrequency";
 
     /**
+     * Annotation key for minimum lambda in nm.
+     * Value is expected to be an integer.
+     */
+    public static final String AK_RX_MIN_LMDA_NM = "rxMinLambda";
+
+    /**
      * Annotation key for maximum frequency in Hz.
      * Value is expected be an integer.
      */
     public static final String AK_RX_MAX_FREQ_HZ = "rxMaxFrequency";
 
     /**
-     * Annotation key for grid in Hz.
+     * Annotation key for maximum lambda in nm.
+     * Value is expected be an integer.
+     */
+    public static final String AK_RX_MAX_LMDA_NM = "rxMaxLambda";
+
+    /**
+     * Annotation key for grid frequency in Hz.
      * Value is expected to be an integer.
      */
     public static final String AK_RX_GRID_HZ = "rxGrid";
 
     /**
+     * Annotation key for grid lambda in nm.
+     * Value is expected to be an integer.
+     */
+    public static final String AK_RX_GRID_LMDA_NM = "rxGridLambda";
+
+    /**
+     * Annotation key for indicating frequency must be used instead of
+     * wavelength for port tuning.
+     * Value is expected to be "enabled" or "disabled"
+     */
+    public static final String AK_USE_FREQ_FEATURE = "useFreqFeature";
+
+    /**
      * Annotation key for minimum transmit power in dBm*10.
      * Value is expected to be an integer.
      */
@@ -226,35 +288,71 @@
     public static final String AK_TX_FREQ_HZ = "txFrequency";
 
     /**
-     * Annotation key for transmit offset in Hz.
+     * Annotation key for transmit lambda in nm.
+     * Value is expected be an integer.
+     */
+    public static final String AK_TX_LMDA_NM = "txLambda";
+
+    /**
+     * Annotation key for transmit offset frequency in Hz.
      * Value is expected be an integer.
      */
     public static final String AK_TX_OFFSET_HZ = "txOffset";
 
     /**
-     * Annotation key for transmit grid spacing in Hz.
+     * Annotation key for transmit offset in nm.
+     * Value is expected be an integer.
+     */
+    public static final String AK_TX_OFFSET_LMDA_NM = "txOffsetLambda";
+
+    /**
+     * Annotation key for transmit grid spacing frequency in Hz.
      * Value is expected be an integer.
      */
     public static final String AK_TX_GRID_SPAN_HZ = "txGridSpan";
 
     /**
+     * Annotation key for transmit grid spacing lambda in nm.
+     * Value is expected be an integer.
+     */
+    public static final String AK_TX_GRID_SPAN_LMDA_NM = "txGridSpanLambda";
+
+    /**
      * Annotation key for receive frequency in Hz.
      * Value is expected be an integer.
      */
     public static final String AK_RX_FREQ_HZ = "rxFrequency";
 
     /**
-     * Annotation key for receive offset in Hz.
+     * Annotation key for receive lambda in nm.
+     * Value is expected be an integer.
+     */
+    public static final String AK_RX_LMDA_NM = "rxLambda";
+
+    /**
+     * Annotation key for receive offset frequency in Hz.
      * Value is expected be an integer.
      */
     public static final String AK_RX_OFFSET_HZ = "rxOffset";
 
     /**
-     * Annotation key for receive grid spacing in Hz.
+     * Annotation key for receive offset lambda in nm.
+     * Value is expected be an integer.
+     */
+    public static final String AK_RX_OFFSET_LMDA_NM = "rxOffsetLambda";
+
+    /**
+     * Annotation key for receive grid spacing frequency in Hz.
      * Value is expected be an integer.
      */
     public static final String AK_RX_GRID_SPAN_HZ = "rxGridSpan";
 
+    /**
+     * Annotation key for receive grid spacing lambda in nm.
+     * Value is expected be an integer.
+     */
+    public static final String AK_RX_GRID_SPAN_LMDA_NM = "rxGridSpanLambda";
+
    /**
      * Annotation key for transmit power in dBm*10.
      * Value is expected to be an integer.
@@ -326,6 +424,8 @@
     private static final Frequency FREQ191_7 = Frequency.ofGHz(191_700);
     private static final Frequency FREQ4_4 = Frequency.ofGHz(4_400);
 
+    private static final long C = 299792458; // speed of light in m/s
+
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected DeviceProviderRegistry providerRegistry;
 
@@ -348,6 +448,12 @@
     label = "Frequency (in seconds) for polling switch Port statistics")
     private int portStatsPollFrequency = POLL_INTERVAL;
 
+    private static final String PROP_FREQ = "propertyFrequency";
+    private static final boolean DEFAULT_PROP_FREQ = true;
+    @Property(name = PROP_FREQ, boolValue = DEFAULT_PROP_FREQ,
+    label = "It indicates frequency must be used instead of wavelength for port tuning.")
+    private static boolean propFreq = DEFAULT_PROP_FREQ;
+
     private final Timer timer = new Timer("onos-openflow-portstats-collector");
 
     private HashMap<Dpid, PortStatsCollector> collectors = Maps.newHashMap();
@@ -509,14 +615,45 @@
         providerService.updatePortStatistics(deviceId, stats);
     }
 
+    private static String lambdaToAnnotationHz(long lambda) {
+        // ref. OF1.5: wavelength (lambda) as nm * 100
+
+        // f = c / λ
+        // (m/s) * (nm/m) / (nm * 100) * 100
+        // annotations is in Hz
+        return Long.toString(lambda == 0 ? lambda : (C * 1_000_000_000 / lambda * 100));
+    }
+
+    private static String mhzToAnnotationNm(long freqMhz) {
+        // λ = c / f
+        // (m/s) * (nm/m) / (1000000 * 1/s)
+        // annotations is in nm
+        return Long.toString(freqMhz == 0 ? freqMhz : (C * 1_000_000_000 / Frequency.ofMHz(freqMhz).asHz()));
+    }
+
 
     private static String mhzToAnnotation(long freqMhz) {
         return Long.toString(Frequency.ofMHz(freqMhz).asHz());
     }
 
+    private static String freqLmdaToAnnotation(long freqLmda, boolean useFreq) {
+        if (useFreq) {
+            if (propFreq) {
+                mhzToAnnotation(freqLmda);
+            } else {
+                mhzToAnnotationNm(freqLmda);
+            }
+        } else if (propFreq) {
+            lambdaToAnnotationHz(freqLmda);
+        }
+        return Double.toString(freqLmda / 100.0);
+    }
+
     private Collection<PortStatistics> buildPortStatistics(DeviceId deviceId,
                                                            List<OFPortStatsEntry> entries) {
         HashSet<PortStatistics> stats = Sets.newHashSet();
+        final Dpid dpid = dpid(deviceId.uri());
+        OpenFlowSwitch sw = controller.getSwitch(dpid);
 
         for (OFPortStatsEntry entry : entries) {
             try {
@@ -533,23 +670,46 @@
                 if (optical.isPresent()) {
                     long flags = optical.get().getFlags();
 
+                    boolean useFreq = false;
+                    for (OFPortDesc pd : sw.getPorts()) {
+                        if (pd.getPortNo().equals(entry.getPortNo())) {
+                            for (OFPortDescProp prop : pd.getProperties()) {
+                                if (prop instanceof OFPortDescPropOptical) {
+                                    OFPortDescPropOptical oprop = (OFPortDescPropOptical) prop;
+                                    long supported = oprop.getSupported();
+                                    int useFreqVal = OFOpticalPortFeaturesSerializerVer14.USE_FREQ_VAL;
+                                    if ((supported & useFreqVal) != 0) {
+                                        useFreq = true;
+                                        break;
+                                    }
+                                }
+                            }
+                        }
+                    }
+
                     int txTune = OFPortStatsOpticalFlagsSerializerVer14.TX_TUNE_VAL;
                     long txFreq = optical.get().getTxFreqLmda();
                     long txOffset = optical.get().getTxOffset();
                     long txGridSpan = optical.get().getTxGridSpan();
                     annotations.set(AK_TX_TUNE_FEATURE, ((flags & txTune) != 0) ? "enabled" : "disabled");
-                    annotations.set(AK_TX_FREQ_HZ, mhzToAnnotation(txFreq));
-                    annotations.set(AK_TX_OFFSET_HZ, mhzToAnnotation(txOffset));
-                    annotations.set(AK_TX_GRID_SPAN_HZ, mhzToAnnotation(txGridSpan));
+                    annotations.set(propFreq ? AK_TX_FREQ_HZ : AK_TX_LMDA_NM,
+                                    freqLmdaToAnnotation(txFreq, useFreq));
+                    annotations.set(propFreq ? AK_TX_OFFSET_HZ : AK_TX_OFFSET_LMDA_NM,
+                                    freqLmdaToAnnotation(txOffset, useFreq));
+                    annotations.set(propFreq ? AK_TX_GRID_SPAN_HZ : AK_TX_GRID_SPAN_LMDA_NM,
+                                    freqLmdaToAnnotation(txGridSpan, useFreq));
 
                     int rxTune = OFPortStatsOpticalFlagsSerializerVer14.RX_TUNE_VAL;
                     long rxFreq = optical.get().getRxFreqLmda();
                     long rxOffset = optical.get().getRxOffset();
                     long rxGridSpan = optical.get().getRxGridSpan();
                     annotations.set(AK_RX_TUNE_FEATURE, ((flags & rxTune) != 0) ? "enabled" : "disabled");
-                    annotations.set(AK_RX_FREQ_HZ, mhzToAnnotation(rxFreq));
-                    annotations.set(AK_RX_OFFSET_HZ, mhzToAnnotation(rxOffset));
-                    annotations.set(AK_RX_GRID_SPAN_HZ, mhzToAnnotation(rxGridSpan));
+                    annotations.set(propFreq ? AK_RX_FREQ_HZ : AK_RX_LMDA_NM,
+                                    freqLmdaToAnnotation(rxFreq, useFreq));
+                    annotations.set(propFreq ? AK_RX_OFFSET_HZ : AK_RX_OFFSET_LMDA_NM,
+                                    freqLmdaToAnnotation(rxOffset, useFreq));
+                    annotations.set(propFreq ? AK_RX_GRID_SPAN_HZ : AK_RX_GRID_SPAN_LMDA_NM,
+                                    freqLmdaToAnnotation(rxGridSpan, useFreq));
 
                     int txPwrVal = OFPortStatsOpticalFlagsSerializerVer14.TX_PWR_VAL;
                     int txPwr = optical.get().getTxPwr();
@@ -986,20 +1146,6 @@
             return builder;
         }
 
-        private String mhzToAnnotation(long freqMhz) {
-            return OpenFlowDeviceProvider.mhzToAnnotation(freqMhz);
-        }
-
-        private String lambdaToAnnotationHz(long lambda) {
-            // ref. OF1.5: wavelength (lambda) as nm * 100
-
-            long c = 299792458; // speed of light in m/s
-            // f = c / λ
-            // (m/s) * (nm/m) / (nm * 100) * 100
-            // annotations is in Hz
-            return Long.toString(lambda == 0 ? lambda : (c * 1_000_000_000 / lambda * 100));
-        }
-
         private PortDescription buildPortDescription14(OFPortDesc port) {
             PortNumber portNo = PortNumber.portNumber(port.getPortNo().getPortNumber());
             boolean enabled =
@@ -1050,57 +1196,36 @@
                         ((supported & rxTune) != 0) ? "enabled" : "disabled");
 
                 // wire value for OFOpticalPortFeatures.USE_FREQ
-                long useFreq = OFOpticalPortFeaturesSerializerVer14.USE_FREQ_VAL;
-                if ((supported & useFreq) != 0) {
-                    // unit is in Frequency Mhz
-                    annotations.set(AK_RX_MIN_FREQ_HZ, mhzToAnnotation(rxMin));
-                    annotations.set(AK_RX_MAX_FREQ_HZ, mhzToAnnotation(rxMax));
-                    annotations.set(AK_RX_GRID_HZ, mhzToAnnotation(rxGrid));
+                boolean useFreq = (supported & OFOpticalPortFeaturesSerializerVer14.USE_FREQ_VAL) != 0;
+                annotations.set(AK_USE_FREQ_FEATURE, useFreq ? "enabled" : "disabled");
 
-                    annotations.set(AK_TX_MIN_FREQ_HZ, mhzToAnnotation(txMin));
-                    annotations.set(AK_TX_MAX_FREQ_HZ, mhzToAnnotation(txMax));
-                    annotations.set(AK_TX_GRID_HZ, mhzToAnnotation(txGrid));
+                annotations.set(propFreq ? AK_RX_MIN_FREQ_HZ : AK_RX_MIN_LMDA_NM,
+                                freqLmdaToAnnotation(rxMin, useFreq));
+                annotations.set(propFreq ? AK_RX_MAX_FREQ_HZ : AK_RX_MAX_LMDA_NM,
+                                freqLmdaToAnnotation(rxMax, useFreq));
+                annotations.set(propFreq ? AK_RX_GRID_HZ : AK_RX_GRID_LMDA_NM,
+                                freqLmdaToAnnotation(rxGrid, useFreq));
 
-                    // FIXME pretty confident this is not going to happen
-                    // unless Device models Tx/Rx ports as separate port
-                    if (rxMin == txMin) {
-                        annotations.set(AK_MIN_FREQ_HZ,
-                                        mhzToAnnotation(rxMin));
-                    }
-                    if (rxMax == txMax) {
-                        annotations.set(AK_MAX_FREQ_HZ,
-                                        mhzToAnnotation(rxMax));
-                    }
-                    if (rxGrid == txGrid) {
-                        annotations.set(AK_GRID_HZ,
-                                        mhzToAnnotation(rxGrid));
-                    }
+                annotations.set(propFreq ? AK_TX_MIN_FREQ_HZ : AK_TX_MIN_LMDA_NM,
+                                freqLmdaToAnnotation(txMin, useFreq));
+                annotations.set(propFreq ? AK_TX_MAX_FREQ_HZ : AK_TX_MAX_LMDA_NM,
+                                freqLmdaToAnnotation(txMax, useFreq));
+                annotations.set(propFreq ? AK_TX_GRID_HZ : AK_TX_GRID_LMDA_NM,
+                                freqLmdaToAnnotation(txGrid, useFreq));
 
-                } else {
-                    // unit is in Lambda nm * 100
-                    annotations.set(AK_RX_MIN_FREQ_HZ, lambdaToAnnotationHz(rxMin));
-                    annotations.set(AK_RX_MAX_FREQ_HZ, lambdaToAnnotationHz(rxMax));
-                    annotations.set(AK_RX_GRID_HZ, lambdaToAnnotationHz(rxGrid));
-
-                    annotations.set(AK_TX_MIN_FREQ_HZ, lambdaToAnnotationHz(txMin));
-                    annotations.set(AK_TX_MAX_FREQ_HZ, lambdaToAnnotationHz(txMax));
-                    annotations.set(AK_TX_GRID_HZ, lambdaToAnnotationHz(txGrid));
-
-                    // FIXME pretty confident this is not going to happen
-                    // unless Device models Tx/Rx ports as separate port
-                    if (rxMin == txMin) {
-                        annotations.set(AK_MIN_FREQ_HZ,
-                                        lambdaToAnnotationHz(rxMin));
-                    }
-                    if (rxMax == txMax) {
-                        annotations.set(AK_MAX_FREQ_HZ,
-                                        lambdaToAnnotationHz(rxMax));
-                    }
-                    if (rxGrid == txGrid) {
-                        annotations.set(AK_GRID_HZ,
-                                        lambdaToAnnotationHz(rxGrid));
-                    }
-
+                // FIXME pretty confident this is not going to happen
+                // unless Device models Tx/Rx ports as separate port
+                if (rxMin == txMin) {
+                    annotations.set(propFreq ? AK_MIN_FREQ_HZ : AK_MIN_LMDA_NM,
+                                    freqLmdaToAnnotation(rxMin, useFreq));
+                }
+                if (rxMax == txMax) {
+                    annotations.set(propFreq ? AK_MAX_FREQ_HZ : AK_MAX_LMDA_NM,
+                                    freqLmdaToAnnotation(rxMax, useFreq));
+                }
+                if (rxGrid == txGrid) {
+                    annotations.set(propFreq ? AK_GRID_HZ : AK_GRID_LMDA_NM,
+                                    freqLmdaToAnnotation(rxGrid, useFreq));
                 }
 
                 int txPwr = OFOpticalPortFeaturesSerializerVer14.TX_PWR_VAL;
diff --git a/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupStatsCollector.java b/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupStatsCollector.java
index 5433a4d..4a5c872 100644
--- a/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupStatsCollector.java
+++ b/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupStatsCollector.java
@@ -19,6 +19,7 @@
 import org.onlab.util.Timer;
 import org.onosproject.openflow.controller.OpenFlowSwitch;
 import org.onosproject.openflow.controller.RoleState;
+import org.projectfloodlight.openflow.protocol.OFCapabilities;
 import org.projectfloodlight.openflow.protocol.OFGroupDescStatsRequest;
 import org.projectfloodlight.openflow.protocol.OFGroupStatsRequest;
 import org.projectfloodlight.openflow.types.OFGroup;
@@ -59,7 +60,7 @@
     public void run(Timeout timeout) throws Exception {
         log.trace("Collecting stats for {}", sw.getStringId());
 
-        sendGroupStatisticRequest();
+        sendGroupStatisticRequests();
 
         if (!this.stopTimer) {
             log.trace("Scheduling stats collection in {} seconds for {}",
@@ -69,7 +70,23 @@
         }
     }
 
-    private void sendGroupStatisticRequest() {
+    private void sendGroupDescStatisticRequest(long xid) {
+        OFGroupDescStatsRequest descStatsRequest =
+                sw.factory().buildGroupDescStatsRequest()
+                        .setXid(xid)
+                        .build();
+        sw.sendMsg(descStatsRequest);
+    }
+
+    private void sendGroupStatisticRequest(long xid) {
+        OFGroupStatsRequest statsRequest = sw.factory().buildGroupStatsRequest()
+            .setGroup(OFGroup.ALL)
+            .setXid(xid)
+            .build();
+        sw.sendMsg(statsRequest);
+    }
+
+    private void sendGroupStatisticRequests() {
         if (log.isTraceEnabled()) {
             log.trace("sendGroupStatistics {}:{}", sw.getStringId(), sw.getRole());
         }
@@ -79,19 +96,15 @@
         if (!sw.isConnected()) {
             return;
         }
-        long statsXid = OpenFlowGroupProvider.getXidAndAdd(2);
-        OFGroupStatsRequest statsRequest = sw.factory().buildGroupStatsRequest()
-                .setGroup(OFGroup.ALL)
-                .setXid(statsXid)
-                .build();
-        sw.sendMsg(statsRequest);
 
-        long descXid = statsXid + 1;
-        OFGroupDescStatsRequest descStatsRequest =
-                sw.factory().buildGroupDescStatsRequest()
-                        .setXid(descXid)
-                        .build();
-        sw.sendMsg(descStatsRequest);
+        if (sw.features().getCapabilities().contains(OFCapabilities.GROUP_STATS)) {
+            long xid = OpenFlowGroupProvider.getXidAndAdd(2);
+            sendGroupDescStatisticRequest(xid);
+            sendGroupStatisticRequest(xid + 1);
+        } else {
+            long xid = OpenFlowGroupProvider.getXidAndAdd(1);
+            sendGroupDescStatisticRequest(xid);
+        }
     }
 
     public void adjustRate(int pollInterval) {
diff --git a/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProvider.java b/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProvider.java
index 0f55b41..b536fda 100644
--- a/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProvider.java
+++ b/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProvider.java
@@ -65,7 +65,6 @@
 import org.onosproject.openflow.controller.RoleState;
 import org.osgi.service.component.ComponentContext;
 import org.projectfloodlight.openflow.protocol.OFBucketCounter;
-import org.projectfloodlight.openflow.protocol.OFCapabilities;
 import org.projectfloodlight.openflow.protocol.OFErrorMsg;
 import org.projectfloodlight.openflow.protocol.OFErrorType;
 import org.projectfloodlight.openflow.protocol.OFGroupDescStatsEntry;
@@ -413,7 +412,7 @@
             if (sw == null) {
                 return;
             }
-            if (isGroupSupported(sw) && sw.features().getCapabilities().contains(OFCapabilities.GROUP_STATS)) {
+            if (isGroupSupported(sw)) {
                 GroupStatsCollector gsc = new GroupStatsCollector(sw, groupPollInterval);
                 stopCollectorIfNeeded(collectors.put(dpid, gsc));
                 gsc.start();
diff --git a/providers/pcep/tunnel/src/main/java/org/onosproject/provider/pcep/tunnel/impl/PcepTunnelProvider.java b/providers/pcep/tunnel/src/main/java/org/onosproject/provider/pcep/tunnel/impl/PcepTunnelProvider.java
index a2d21bc..d1afdc6 100644
--- a/providers/pcep/tunnel/src/main/java/org/onosproject/provider/pcep/tunnel/impl/PcepTunnelProvider.java
+++ b/providers/pcep/tunnel/src/main/java/org/onosproject/provider/pcep/tunnel/impl/PcepTunnelProvider.java
@@ -217,7 +217,7 @@
 
     TunnelProviderService service;
 
-    HashMap<String, TunnelId> tunnelMap = new HashMap<String, TunnelId>();
+    HashMap<String, TunnelId> tunnelMap = new HashMap<>();
     HashMap<TunnelId, TunnelStatistics> tunnelStatisticsMap = new HashMap<>();
     private HashMap<String, TunnelStatsCollector> collectors = Maps.newHashMap();
 
@@ -885,7 +885,7 @@
      * @return list of ERO subobjects
      */
     private LinkedList<PcepValueType> createPcepPath(Path path) {
-        LinkedList<PcepValueType> llSubObjects = new LinkedList<PcepValueType>();
+        LinkedList<PcepValueType> llSubObjects = new LinkedList<>();
         List<Link> listLink = path.links();
         ConnectPoint source = null;
         ConnectPoint destination = null;
@@ -952,7 +952,7 @@
             return null;
         }
 
-        LinkedList<PcepValueType> llOptionalTlv = new LinkedList<PcepValueType>();
+        LinkedList<PcepValueType> llOptionalTlv = new LinkedList<>();
 
         // set PathSetupTypeTlv of SRP object
         tlv = new PathSetupTypeTlv(lspType.type());
@@ -962,8 +962,8 @@
         PcepSrpObject srpobj = pc.factory().buildSrpObject().setSrpID(srpId).setRFlag(false)
                 .setOptionalTlv(llOptionalTlv).build();
 
-        llOptionalTlv = new LinkedList<PcepValueType>();
-        LinkedList<PcInitiatedLspRequest> llPcInitiatedLspRequestList = new LinkedList<PcInitiatedLspRequest>();
+        llOptionalTlv = new LinkedList<>();
+        LinkedList<PcInitiatedLspRequest> llPcInitiatedLspRequestList = new LinkedList<>();
 
         // set LSP identifiers TLV
         short localLspId = 0;
@@ -1089,7 +1089,7 @@
             int srpId = SrpIdGenerators.create();
 
             PcepValueType tlv;
-            LinkedList<PcepValueType> llOptionalTlv = new LinkedList<PcepValueType>();
+            LinkedList<PcepValueType> llOptionalTlv = new LinkedList<>();
 
             // set PathSetupTypeTlv of SRP object
             tlv = new PathSetupTypeTlv(LspType.valueOf(tunnel.annotations().value(LSP_SIG_TYPE))
@@ -1100,8 +1100,8 @@
             PcepSrpObject srpobj = pc.factory().buildSrpObject().setSrpID(srpId).setRFlag(true)
                     .setOptionalTlv(llOptionalTlv).build();
 
-            llOptionalTlv = new LinkedList<PcepValueType>();
-            LinkedList<PcInitiatedLspRequest> llPcInitiatedLspRequestList = new LinkedList<PcInitiatedLspRequest>();
+            llOptionalTlv = new LinkedList<>();
+            LinkedList<PcInitiatedLspRequest> llPcInitiatedLspRequestList = new LinkedList<>();
 
             tlv = new SymbolicPathNameTlv(tunnel.tunnelName().value().getBytes());
             llOptionalTlv.add(tlv);
@@ -1183,8 +1183,8 @@
                 llSubObjects = createPcepPath(path);
             }
 
-            LinkedList<PcepValueType> llOptionalTlv = new LinkedList<PcepValueType>();
-            LinkedList<PcepUpdateRequest> llUpdateRequestList = new LinkedList<PcepUpdateRequest>();
+            LinkedList<PcepValueType> llOptionalTlv = new LinkedList<>();
+            LinkedList<PcepUpdateRequest> llUpdateRequestList = new LinkedList<>();
 
             // set PathSetupTypeTlv of SRP object
             tlv = new PathSetupTypeTlv(lspSigType.type());
@@ -1194,7 +1194,7 @@
             PcepSrpObject srpobj = pc.factory().buildSrpObject().setSrpID(srpId).setRFlag(false)
                     .setOptionalTlv(llOptionalTlv).build();
 
-            llOptionalTlv = new LinkedList<PcepValueType>();
+            llOptionalTlv = new LinkedList<>();
 
             // Lsp Identifier tlv is required for all modes of lsp
             String localLspIdString = tunnel.annotations().value(LOCAL_LSP_ID);
@@ -1609,7 +1609,7 @@
             DefaultTunnelDescription td;
             SparseAnnotations annotations = null;
             State tunnelState = PcepLspStatus.getTunnelStatusFromLspStatus(PcepLspStatus.values()[lspObj.getOFlag()]);
-            if (tunnel == null) {
+            if (tunnel == null && pathNameTlv != null) {
                 if (lspObj.getRFlag()) {
                     /*
                      * If PCC sends remove message and for any reason PCE does not have that entry, simply discard the
@@ -1661,7 +1661,7 @@
                 if (mastershipService.isLocalMaster(deviceId)) {
                     TunnelId tId = tunnelAdded(td, tunnelState);
                     Tunnel tunnelInserted = new DefaultTunnel(providerId, tunnelEndPointSrc, tunnelEndPointDst, MPLS,
-                            tunnelState, new GroupId(0), tId, TunnelName.tunnelName(String.valueOf(pathNameTlv
+                            tunnelState, new GroupId(0), tId, TunnelName.tunnelName(Arrays.toString(pathNameTlv
                                     .getValue())), path, labelStack, annotations);
 
                     PcepTunnelData pcepTunnelData = new PcepTunnelData(tunnelInserted, path, LSP_STATE_RPT);
diff --git a/providers/rest/device/src/main/java/org/onosproject/provider/rest/device/impl/RestDeviceProvider.java b/providers/rest/device/src/main/java/org/onosproject/provider/rest/device/impl/RestDeviceProvider.java
index 3e97a3d..5e33c1d 100644
--- a/providers/rest/device/src/main/java/org/onosproject/provider/rest/device/impl/RestDeviceProvider.java
+++ b/providers/rest/device/src/main/java/org/onosproject/provider/rest/device/impl/RestDeviceProvider.java
@@ -164,7 +164,6 @@
     @Deactivate
     public void deactivate() {
         cfgService.removeListener(configListener);
-        controller.getDevices().keySet().forEach(this::deviceRemoved);
         providerRegistry.unregister(this);
         providerService = null;
         factories.forEach(cfgService::unregisterConfigFactory);
diff --git a/tools/build/conf/src/main/java/org/onosproject/buckdaemon/BuckDaemon.java b/tools/build/conf/src/main/java/org/onosproject/buckdaemon/BuckDaemon.java
index 56deadd..f3166b1 100644
--- a/tools/build/conf/src/main/java/org/onosproject/buckdaemon/BuckDaemon.java
+++ b/tools/build/conf/src/main/java/org/onosproject/buckdaemon/BuckDaemon.java
@@ -175,7 +175,11 @@
             try {
                 try {
                     socket.setSoTimeout(1_000); //reads should time out after 1 second
-                    BuckTaskContext context = new BuckTaskContext(socket.getInputStream());
+                    BuckTaskContext context = BuckTaskContext.createBuckTaskContext(socket.getInputStream());
+                    if (context == null) {
+                        socket.close();
+                        return;
+                    }
 
                     String taskName = context.taskName();
                     BuckTask task = tasks.get(taskName);
diff --git a/tools/build/conf/src/main/java/org/onosproject/buckdaemon/BuckTaskContext.java b/tools/build/conf/src/main/java/org/onosproject/buckdaemon/BuckTaskContext.java
index b8d189c..2cfb35d 100644
--- a/tools/build/conf/src/main/java/org/onosproject/buckdaemon/BuckTaskContext.java
+++ b/tools/build/conf/src/main/java/org/onosproject/buckdaemon/BuckTaskContext.java
@@ -36,10 +36,16 @@
     private final ImmutableList<String> input;
     private final List<String> output;
 
-    BuckTaskContext(InputStream inputStream) throws IOException {
+    public static BuckTaskContext createBuckTaskContext(InputStream inputStream) throws IOException {
         ImmutableList<String> lines = slurpInput(inputStream);
-        checkArgument(lines.size() >= 1 && !lines.get(0).isEmpty(),
-                "Request must contain at least task type");
+        if (lines.size() == 0) {
+            return null;
+        } else {
+            return new BuckTaskContext(lines);
+        }
+    }
+
+    BuckTaskContext(ImmutableList<String> lines) {
         this.taskName = lines.get(0);
         this.input = lines.subList(1, lines.size());
         this.output = Lists.newArrayList();
@@ -55,7 +61,7 @@
     private static ImmutableList<String> slurpInput(InputStream stream) throws IOException {
         ImmutableList.Builder<String> lines = ImmutableList.builder();
         BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(stream));
-        while(true) {
+        while (true) {
             String line = bufferedReader.readLine();
             if (line == null || line.trim().length() == 0) {
                 // Empty line or EOF
diff --git a/tools/build/onos-buck b/tools/build/onos-buck
index b44dc17..7312f72 100755
--- a/tools/build/onos-buck
+++ b/tools/build/onos-buck
@@ -5,8 +5,8 @@
 
 set -e
 
-BUCK_URL="http://repo1.maven.org/maven2/org/onosproject/onos-buck/v2018.01.10.02/buck-v2018.01.10.02.zip"
-BUCK_SHA="04ac8754c6ca5957ac68a75544d1bd0c9d17b908"
+BUCK_URL="http://repo1.maven.org/maven2/org/onosproject/onos-buck/v2018.01.17.01/buck-v2018.01.17.01.zip"
+BUCK_SHA="4a70a84ef40a06762a7248db25590696e80af9fe"
 
 [  "-U" = "$1" ] && shift && FORCE_UPDATE=True
 
diff --git a/tools/build/onos-upload-bits b/tools/build/onos-upload-bits
index 367fc1d..d74deb8 100755
--- a/tools/build/onos-upload-bits
+++ b/tools/build/onos-upload-bits
@@ -8,8 +8,6 @@
 [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
 . $ONOS_ROOT/tools/build/envDefaults
 
-#FIXME need to export s3Creds
-
 # Stage the onos tar in /tmp
 rm -f $ONOS_TAR
 cp $(onos-buck build onos --show-output | tail -1 | cut -d\  -f2) $ONOS_TAR
@@ -25,4 +23,15 @@
 rm -f $ONOS_TEST_TAR
 cp $(onos-buck build //:onos-test --show-output | tail -1 | cut -d\  -f2) $ONOS_TEST_TAR
 
-onosUploadBits.py ${ONOS_VERSION%-*}
+# use this to upload to AWS
+# onosUploadBits.py ${ONOS_VERSION%-*}
+
+# use this to upload to maven central
+if echo $ONOS_VERSION | grep '-'; then
+    echo "ONOS version $ONOS_VERSION is a beta or RC. Skipping publishing .tar.gz files"
+    exit 0
+fi
+UPLOAD_BASE="https://oss.sonatype.org/service/local/staging/deploy/maven2/org/onosproject/onos-releases/$ONOS_VERSION"
+curl -v -u "$SONATYPE_USER:$SONATYPE_PASSWORD" --upload-file $ONOS_TAR $UPLOAD_BASE/onos-$ONOS_VERSION.tar.gz
+curl -v -u "$SONATYPE_USER:$SONATYPE_PASSWORD" --upload-file $ONOS_TEST_TAR $UPLOAD_BASE/onos-test-$ONOS_VERSION.tar.gz
+curl -v -u "$SONATYPE_USER:$SONATYPE_PASSWORD" --upload-file $ONOS_ZIP $UPLOAD_BASE/onos-$ONOS_VERSION.zip
\ No newline at end of file
diff --git a/tools/build/onos-upload-docs b/tools/build/onos-upload-docs
index 79986b6..1dc750a 100755
--- a/tools/build/onos-upload-docs
+++ b/tools/build/onos-upload-docs
@@ -13,6 +13,24 @@
 
 docs=$(onos-buck build //docs:external --show-output 2>/dev/null | tail -1 | cut -d\  -f2)
 
+ONOS_VERSION_STRING=$ONOS_VERSION
+if echo $ONOS_VERSION_STRING | grep '-'; then
+    echo "ONOS version $ONOS_VERSION_STRING is a beta or RC. Skipping"
+    exit 0
+fi
+
+scp $remote:/var/www/api/index.html /tmp/index.html
+CURRENT_VERSION_STRING=`grep URL /tmp/index.html | sed "s%.*URL=/%%" | sed "s%/.*%%"`
+CURRENT_VERSION_SPLIT=(${CURRENT_VERSION_STRING//\./ })
+ONOS_VERSION_SPLIT=(${ONOS_VERSION_STRING//\./ })
+
+if (( ${ONOS_VERSION_SPLIT[1]} >= ${CURRENT_VERSION_SPLIT[1]} )); then
+    echo "Should replace current version $CURRENT_VERSION_STRING with new ONOS version $ONOS_VERSION_STRING"
+else
+    echo "Not replacing current version $CURRENT_VERSION_STRING with ONOS version $ONOS_VERSION_STRING"
+    exit 0
+fi
+
 scp $docs $remote:/tmp/onos-apidocs-$ONOS_VERSION.tar.gz
 ssh $remote "
     mkdir -p /var/www/api/$ONOS_VERSION
diff --git a/tools/dev/bash_profile b/tools/dev/bash_profile
index 6d38269..3dc3f89 100644
--- a/tools/dev/bash_profile
+++ b/tools/dev/bash_profile
@@ -195,7 +195,11 @@
 
 # ON.Lab shared test cell warden address
 export CELL_WARDEN="10.192.19.72"
-export CELL_SLAVES="$CELL_WARDEN 10.192.19.71 10.192.19.70"
+export CELL_SLAVE_1=$CELL_WARDEN
+export CELL_SLAVE_2="10.192.19.71"
+export CELL_SLAVE_3="10.192.19.70"
+export CELL_SLAVE_4="10.192.19.77"
+export CELL_SLAVES="$CELL_SLAVE_1 $CELL_SLAVE_2 $CELL_SLAVE_3 $CELL_SLAVE_4"
 
 # Clears cell environment
 function clearCell {
diff --git a/tools/dev/mininet/bmv2.py b/tools/dev/mininet/bmv2.py
index b1824a3..87ed1ba 100644
--- a/tools/dev/mininet/bmv2.py
+++ b/tools/dev/mininet/bmv2.py
@@ -2,9 +2,11 @@
 import socket
 import re
 import json
+import threading
 import urllib2
 
 import time
+from contextlib import closing
 
 from mininet.log import info, warn, error
 from mininet.node import Switch, Host
@@ -13,12 +15,13 @@
     error("ERROR: environment var $ONOS_ROOT not set")
     exit()
 
-BMV2_TARGET = 'simple_switch_grpc'
+SIMPLE_SWITCH_GRPC = 'simple_switch_grpc'
 ONOS_ROOT = os.environ["ONOS_ROOT"]
-CPU_PORT = 255
 PKT_BYTES_TO_DUMP = 80
 VALGRIND_PREFIX = 'valgrind --leak-check=yes'
-SWITCH_START_TIMEOUT = 5 #seconds
+SWITCH_START_TIMEOUT = 5  # seconds
+BMV2_LOG_LINES = 5
+
 
 def parseBoolean(value):
     if value in ['1', 1, 'true', 'True']:
@@ -27,6 +30,34 @@
         return False
 
 
+def pickUnusedPort():
+    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    s.bind(('localhost', 0))
+    addr, port = s.getsockname()
+    s.close()
+    return port
+
+
+def writeToFile(path, value):
+    with open(path, "w") as f:
+        f.write(str(value))
+
+
+def watchDog(sw):
+    while True:
+        if sw.stopped:
+            return
+        with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
+            if s.connect_ex(('127.0.0.1', sw.grpcPort)) == 0:
+                time.sleep(1)
+            else:
+                warn("\n*** WARN: BMv2 instance %s (%s) died!\n"
+                     % (sw.deviceId, sw.name))
+                sw.printBmv2Log()
+                print ("-" * 80) + "\n"
+                return
+
+
 class ONOSHost(Host):
     def __init__(self, name, inNamespace=True, **params):
         Host.__init__(self, name, inNamespace=inNamespace, **params)
@@ -34,7 +65,7 @@
     def config(self, **params):
         r = super(Host, self).config(**params)
         for off in ["rx", "tx", "sg"]:
-            cmd = "/sbin/ethtool --offload %s %s off"\
+            cmd = "/sbin/ethtool --offload %s %s off" \
                   % (self.defaultIntf(), off)
             self.cmd(cmd)
         # disable IPv6
@@ -48,83 +79,58 @@
     """BMv2 software switch with gRPC server"""
 
     deviceId = 0
-    instanceCount = 0
 
-    def __init__(self, name, json=None, debugger=False, loglevel="warn", elogger=False,
-                 persistent=False, grpcPort=None, thriftPort=None, netcfg=True, dryrun=False,
-                 pipeconfId="", pktdump=False, valgrind=False, netcfgDelay=0,
-                 **kwargs):
+    def __init__(self, name, json=None, debugger=False, loglevel="warn",
+                 elogger=False, grpcPort=None, cpuPort=255,
+                 thriftPort=None, netcfg=True, dryrun=False, pipeconfId="",
+                 pktdump=False, valgrind=False, injectPorts=False, **kwargs):
         Switch.__init__(self, name, **kwargs)
-        self.grpcPort = ONOSBmv2Switch.pickUnusedPort() if not grpcPort else grpcPort
-        self.thriftPort = ONOSBmv2Switch.pickUnusedPort() if not thriftPort else thriftPort
+        self.grpcPort = pickUnusedPort() if not grpcPort else grpcPort
+        self.thriftPort = pickUnusedPort() if not thriftPort else thriftPort
         if self.dpid:
             self.deviceId = int(self.dpid, 0 if 'x' in self.dpid else 16)
         else:
             self.deviceId = ONOSBmv2Switch.deviceId
             ONOSBmv2Switch.deviceId += 1
+        self.cpuPort = cpuPort
         self.json = json
         self.debugger = parseBoolean(debugger)
         self.loglevel = loglevel
-        self.logfile = '/tmp/bmv2-%d.log' % self.deviceId
+        # Important: Mininet removes all /tmp/*.log files in case of exceptions.
+        # We want to be able to see the bmv2 log if anything goes wrong, hence
+        # avoid the .log extension.
+        self.logfile = '/tmp/bmv2-%d-log' % self.deviceId
         self.elogger = parseBoolean(elogger)
         self.pktdump = parseBoolean(pktdump)
-        self.persistent = parseBoolean(persistent)
         self.netcfg = parseBoolean(netcfg)
         self.dryrun = parseBoolean(dryrun)
         self.valgrind = parseBoolean(valgrind)
-        self.netcfgDelay = netcfgDelay
         self.netcfgfile = '/tmp/bmv2-%d-netcfg.json' % self.deviceId
         self.pipeconfId = pipeconfId
-        if persistent:
-            self.exectoken = "/tmp/bmv2-%d-exec-token" % self.deviceId
-            self.cmd("touch %s" % self.exectoken)
-        # Store thrift port for future uses.
-        self.cmd("echo %d > /tmp/bmv2-%d-grpc-port" % (self.grpcPort, self.deviceId))
-
-        if 'longitude' in kwargs:
-            self.longitude = kwargs['longitude']
-        else:
-            self.longitude = None
-
-        if 'latitude' in kwargs:
-            self.latitude = kwargs['latitude']
-        else:
-            self.latitude = None
-
+        self.injectPorts = parseBoolean(injectPorts)
+        self.longitude = kwargs['longitude'] if 'longitude' in kwargs else None
+        self.latitude = kwargs['latitude'] if 'latitude' in kwargs else None
         self.onosDeviceId = "device:bmv2:%d" % self.deviceId
+        self.logfd = None
+        self.bmv2popen = None
+        self.stopped = False
 
-    @classmethod
-    def pickUnusedPort(cls):
-        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-        s.bind(('localhost', 0))
-        addr, port = s.getsockname()
-        s.close()
-        return port
+        # Remove files from previous executions
+        self.cleanupTmpFiles()
+
+        writeToFile("/tmp/bmv2-%d-grpc-port" % self.deviceId, self.grpcPort)
+        writeToFile("/tmp/bmv2-%d-thrift-port" % self.deviceId, self.thriftPort)
 
     def getSourceIp(self, dstIP):
         """
-        Queries the Linux routing table to get the source IP that can talk with dstIP, and vice
-        versa.
+        Queries the Linux routing table to get the source IP that can talk with
+        dstIP, and vice versa.
         """
         ipRouteOut = self.cmd('ip route get %s' % dstIP)
         r = re.search(r"src (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})", ipRouteOut)
         return r.group(1) if r else None
 
     def getDeviceConfig(self, srcIP):
-        portData = {}
-        portId = 1
-        for intfName in self.intfNames():
-            if intfName == 'lo':
-                continue
-            portData[str(portId)] = {
-                "number": portId,
-                "name": intfName,
-                "enabled": True,
-                "removed": False,
-                "type": "copper",
-                "speed": 10000
-            }
-            portId += 1
 
         basicCfg = {
             "driver": "bmv2"
@@ -141,15 +147,36 @@
                     "port": self.grpcPort,
                     "deviceId": self.deviceId,
                     "deviceKeyId": "p4runtime:%s" % self.onosDeviceId
+                },
+                "gnmi": {
+                    "ip": srcIP,
+                    "port": self.grpcPort
                 }
             },
             "piPipeconf": {
                 "piPipeconfId": self.pipeconfId
             },
-            "basic": basicCfg,
-            "ports": portData
+            "basic": basicCfg
         }
 
+        if(self.injectPorts):
+            portData = {}
+            portId = 1
+            for intfName in self.intfNames():
+                if intfName == 'lo':
+                    continue
+                portData[str(portId)] = {
+                    "number": portId,
+                    "name": intfName,
+                    "enabled": True,
+                    "removed": False,
+                    "type": "copper",
+                    "speed": 10000
+                }
+                portId += 1
+
+            cfgData['ports'] = portData
+
         return cfgData
 
     def doOnosNetcfg(self, controllerIP):
@@ -158,7 +185,7 @@
         """
         srcIP = self.getSourceIp(controllerIP)
         if not srcIP:
-            warn("WARN: unable to get device IP address, won't do onos-netcfg")
+            warn("*** WARN: unable to get switch IP address, won't do netcfg\n")
             return
 
         cfgData = {
@@ -177,19 +204,52 @@
         url = 'http://%s:8181/onos/v1/network/configuration/' % controllerIP
         # Instantiate password manager for HTTP auth
         pm = urllib2.HTTPPasswordMgrWithDefaultRealm()
-        pm.add_password(None, url, os.environ['ONOS_WEB_USER'], os.environ['ONOS_WEB_PASS'])
-        urllib2.install_opener(urllib2.build_opener(urllib2.HTTPBasicAuthHandler(pm)))
+        pm.add_password(None, url,
+                        os.environ['ONOS_WEB_USER'],
+                        os.environ['ONOS_WEB_PASS'])
+        urllib2.install_opener(urllib2.build_opener(
+            urllib2.HTTPBasicAuthHandler(pm)))
         # Push config data to controller
-        req = urllib2.Request(url, json.dumps(cfgData), {'Content-Type': 'application/json'})
+        req = urllib2.Request(url, json.dumps(cfgData),
+                              {'Content-Type': 'application/json'})
         try:
             f = urllib2.urlopen(req)
             print f.read()
             f.close()
         except urllib2.URLError as e:
-            warn("WARN: unable to push config to ONOS (%s)" % e.reason)
+            warn("*** WARN: unable to push config to ONOS (%s)\n" % e.reason)
 
     def start(self, controllers):
-        args = [BMV2_TARGET, '--device-id %s' % str(self.deviceId)]
+        bmv2Args = [SIMPLE_SWITCH_GRPC] + self.grpcTargetArgs()
+        if self.valgrind:
+            bmv2Args = VALGRIND_PREFIX.split() + bmv2Args
+
+        cmdString = " ".join(bmv2Args)
+
+        if self.dryrun:
+            info("\n*** DRY RUN (not executing bmv2)")
+
+        info("\nStarting BMv2 target: %s\n" % cmdString)
+
+        try:
+            if not self.dryrun:
+                # Start the switch
+                self.logfd = open(self.logfile, "w")
+                self.bmv2popen = self.popen(cmdString,
+                                            stdout=self.logfd,
+                                            stderr=self.logfd)
+                self.waitBmv2Start()
+                # We want to be notified if BMv2 dies...
+                threading.Thread(target=watchDog, args=[self]).start()
+
+            self.doOnosNetcfg(self.controllerIp(controllers))
+        except Exception as ex:
+            self.killBmv2()
+            self.printBmv2Log()
+            raise ex
+
+    def grpcTargetArgs(self):
+        args = ['--device-id %s' % str(self.deviceId)]
         for port, intf in self.intfs.items():
             if not intf.IP():
                 args.append('-i %d@%s' % (port, intf.name))
@@ -200,74 +260,73 @@
             args.append('--debugger')
         args.append('--log-console')
         if self.pktdump:
-            args.append('--pcap --dump-packet-data %d' % PKT_BYTES_TO_DUMP)
+            args.append('--pcap --dump-packet-data %s' % PKT_BYTES_TO_DUMP)
         args.append('-L%s' % self.loglevel)
-        args.append('--thrift-port %d' % self.thriftPort)
+        args.append('--thrift-port %s' % self.thriftPort)
         if not self.json:
             args.append('--no-p4')
         else:
             args.append(self.json)
-
-        # gRPC target-specific options.
+        # gRPC target-specific options
         args.append('--')
-        args.append('--cpu-port %d' % CPU_PORT)
-        args.append('--grpc-server-addr 0.0.0.0:%d' % self.grpcPort)
+        args.append('--cpu-port %s' % self.cpuPort)
+        args.append('--grpc-server-addr 0.0.0.0:%s' % self.grpcPort)
+        return args
 
-        bmv2cmd = " ".join(args)
-        if self.valgrind:
-            bmv2cmd = "%s %s" % (VALGRIND_PREFIX, bmv2cmd)
-        if self.dryrun:
-            info("\n*** DRY RUN (not executing bmv2)")
-        info("\nStarting BMv2 target: %s\n" % bmv2cmd)
+    def waitBmv2Start(self):
+        # Wait for switch to open gRPC port, before sending ONOS the netcfg.
+        # Include time-out just in case something hangs.
+        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        endtime = time.time() + SWITCH_START_TIMEOUT
+        while True:
+            result = sock.connect_ex(('127.0.0.1', self.grpcPort))
+            if result == 0:
+                # The port is open. Let's go! (Close socket first)
+                sock.close()
+                break
+            # Port is not open yet. If there is time, we wait a bit.
+            if endtime > time.time():
+                time.sleep(0.2)
+            else:
+                # Time's up.
+                raise Exception("Switch did not start before timeout")
 
-        if self.persistent:
-            # Bash loop to re-exec the switch if it crashes.
-            bmv2cmd = "(while [ -e {} ]; do {} ; sleep 1; done;)".format(self.exectoken, bmv2cmd)
+    def printBmv2Log(self):
+        if os.path.isfile(self.logfile):
+            print "-" * 80
+            print "BMv2 %d log (from %s):" % (self.deviceId, self.logfile)
+            with open(self.logfile, 'r') as f:
+                lines = f.readlines()
+                if len(lines) > BMV2_LOG_LINES:
+                    print "..."
+                for line in lines[-BMV2_LOG_LINES:]:
+                    print line.rstrip()
 
-        cmdStr = "{} > {} 2>&1 &".format(bmv2cmd, self.logfile)
-
-        # Starts the switch.
-        if not self.dryrun:
-            out = self.cmd(cmdStr)
-            if out:
-                print out
-            if self.netcfg:
-                time.sleep(self.netcfgDelay)
-
-        try:  # onos.py
+    @staticmethod
+    def controllerIp(controllers):
+        try:
+            # onos.py
             clist = controllers[0].nodes()
         except AttributeError:
             clist = controllers
         assert len(clist) > 0
-        cip = clist[0].IP()
+        return clist[0].IP()
 
-        # Wait for switch to open gRPC port, before sending ONOS the netcfg.json.
-        # Include time-out just in case something hangs.
-        if not self.dryrun:
-            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-            endtime = time.time() + SWITCH_START_TIMEOUT
-            while True:
-                result = sock.connect_ex(('127.0.0.1', self.grpcPort))
-                if result == 0:
-                    # The port is open. Let's go! (Close socket first)
-                    sock.close()
-                    break
-                # Port is not open yet. If there is time, we wait a bit.
-                if endtime > time.time():
-                    time.sleep(0.2)
-                else:
-                    # Time's up.
-                    raise Exception("Switch did not start before {} second timeout. Exiting.\n"
-                          .format(SWITCH_START_TIMEOUT))
-                    exit()
+    def killBmv2(self, log=False):
+        if self.bmv2popen is not None:
+            self.bmv2popen.kill()
+        if self.logfd is not None:
+            if log:
+                self.logfd.write("*** PROCESS TERMINATED BY MININET ***\n")
+            self.logfd.close()
 
-        self.doOnosNetcfg(cip)
+    def cleanupTmpFiles(self):
+        self.cmd("rm -f /tmp/bmv2-%d-*" % self.deviceId)
 
     def stop(self, deleteIntfs=True):
         """Terminate switch."""
-        self.cmd("rm -f /tmp/bmv2-%d-*" % self.deviceId)
-        self.cmd("rm -f /tmp/bmv2-%d.log*" % self.deviceId)
-        self.cmd('kill %' + BMV2_TARGET)
+        self.stopped = True
+        self.killBmv2(log=True)
         Switch.stop(self, deleteIntfs)
 
 
diff --git a/tools/dev/p4vm/bm-commands.sh b/tools/dev/p4vm/bm-commands.sh
new file mode 100644
index 0000000..ee96f22
--- /dev/null
+++ b/tools/dev/p4vm/bm-commands.sh
@@ -0,0 +1,52 @@
+#!/usr/bin/env bash
+
+BUILD_DIR=~/p4tools
+
+export BMV2_PATH=${BUILD_DIR}/bmv2
+export P4RUNTIME_PATH=${BUILD_DIR}/p4runtime
+
+bm-cli () {
+    if [ -z "$1" ]; then
+        echo "No argument supplied. Usage: bm-cli <BMV2 DEVICE ID>"
+        return
+    fi
+    tport=$(head -n 1 /tmp/bmv2-$1-thrift-port)
+    echo "Starting CLI for BMv2 instance $1 (Thrift port $tport)..."
+    sudo ${BMV2_PATH}/tools/runtime_CLI.py --thrift-port ${tport} ${@:2}
+}
+
+bm-dbg () {
+    if [ -z "$1" ]; then
+        echo "No argument supplied. Usage: bm-dbg <BMV2 DEVICE ID>"
+        return
+    fi
+    tport=$(head -n 1 /tmp/bmv2-$1-thrift-port)
+    echo "Starting debugger for BMv2 instance $1 (Thrift port $tport)..."
+    sudo ${BMV2_PATH}/tools/p4dbg.py --thrift-port ${tport} ${@:2}
+}
+
+bm-nmsg () {
+    if [ -z "$1" ]; then
+        echo "No argument supplied. Usage: bm-nmsg <BMV2 DEVICE ID>"
+        return
+    fi
+    tport=$(head -n 1 /tmp/bmv2-$1-thrift-port)
+    echo "Starting nanomsg event listener for BMv2 instance $1 (Thrift port $tport)..."
+    sudo ${BMV2_PATH}/tools/nanomsg_client.py --thrift-port ${tport} ${@:2}
+}
+
+bm-log () {
+    if [ -z "$1" ]; then
+        echo "No argument supplied. Usage: bm-log <BMV2 DEVICE ID>"
+        return
+    fi
+    echo "Showing log for BMv2 instance $1..."
+    echo "---"
+    tail -f /tmp/bmv2-$1-log
+}
+
+bm-sysrepo-reset () {
+    echo "Resetting sysrepo data store..."
+    sudo rm -rf /etc/sysrepo/data/*
+    sudo ${P4RUNTIME_PATH}/proto/sysrepo/install_yangs.sh
+}
diff --git a/tools/dev/p4vm/root-bootstrap.sh b/tools/dev/p4vm/root-bootstrap.sh
index 3f1b8e6..ff23fc5 100755
--- a/tools/dev/p4vm/root-bootstrap.sh
+++ b/tools/dev/p4vm/root-bootstrap.sh
@@ -7,6 +7,7 @@
 echo "sdn ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/99_sdn
 chmod 440 /etc/sudoers.d/99_sdn
 usermod -aG vboxsf sdn
+update-locale LC_ALL="en_US.UTF-8"
 
 # Java 8
 apt-get install software-properties-common -y
@@ -17,7 +18,11 @@
 apt-get -y install \
     oracle-java8-installer oracle-java8-set-default \
     zip unzip \
-    bridge-utils
+    bridge-utils \
+    avahi-daemon \
+    htop \
+    valgrind \
+    git-review
 
 tee -a /etc/ssh/sshd_config <<EOF
 
diff --git a/tools/dev/p4vm/user-bootstrap.sh b/tools/dev/p4vm/user-bootstrap.sh
index 9b19f26..b7a7739 100755
--- a/tools/dev/p4vm/user-bootstrap.sh
+++ b/tools/dev/p4vm/user-bootstrap.sh
@@ -12,6 +12,7 @@
 # ONOS
 export ONOS_ROOT=~/onos
 source ~/onos/tools/dev/bash_profile
+source ~/onos/tools/dev/p4vm/bm-commands.sh
 EOF
 source ~/.profile
 
diff --git a/tools/test/bin/onos-fetch-db b/tools/test/bin/onos-fetch-db
new file mode 100755
index 0000000..0073465
--- /dev/null
+++ b/tools/test/bin/onos-fetch-db
@@ -0,0 +1,43 @@
+#!/bin/bash
+# -----------------------------------------------------------------------------
+# Collect ONOS data from a single node or the current ONOS cell.
+# -----------------------------------------------------------------------------
+
+[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
+. $ONOS_ROOT/tools/build/envDefaults
+
+function print_usage {
+    command_name=`basename $0`
+    echo "Collect ONOS data from a single node or the current ONOS cell."
+    echo
+    echo "Usage:     $command_name <TARGET> "
+    echo "           $command_name [-h | --help]"
+    echo "Options:"
+    echo "    TARGET          The target of the command"
+    echo "    [-h | --help]   Print this help"
+    echo ""
+    echo "TARGET:  <hostname | --cell>"
+    echo "      hostname        Execute on the specified host name"
+    echo "        --cell        Execute on the current ONOS cell"
+    echo ""
+}
+
+# Print usage
+if [ "${1}" = "-h" -o "${1}" = "--help" ]; then
+    print_usage
+    exit 0
+fi
+
+# Select the target
+if [ "${1}" = "--cell" ]; then
+    nodes=$(env | sort | egrep "^OC[0-9]+" | cut -d= -f2)
+else
+    nodes=${1:-$OCI}
+fi
+
+# Execute the remote commands
+for node in $nodes; do
+    echo "fetching from ${node}..."
+    mkdir -p ${node}
+    scp -p -r $ONOS_USER@${node}:$ONOS_INSTALL_DIR/karaf/data/db/partitions/* ./${node}/
+done
diff --git a/tools/test/bin/onos-verify-cell b/tools/test/bin/onos-verify-cell
index 33f9565..7361616 100755
--- a/tools/test/bin/onos-verify-cell
+++ b/tools/test/bin/onos-verify-cell
@@ -7,5 +7,5 @@
 . $ONOS_ROOT/tools/build/envDefaults
 
 for node in $OCT $OCN $(env | sort | egrep "^OC[0-9]+" | cut -d= -f2); do
-    printf "%s: " $node; ssh -n -o StrictHostKeyChecking=no -o PasswordAuthentication=no $ONOS_USER@$node hostname
+    printf "%s: " $node; ssh -n -q -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o PasswordAuthentication=no $ONOS_USER@$node hostname
 done
diff --git a/tools/test/topos/geant.py b/tools/test/topos/geant.py
index e2e6fb7..6eedd29 100644
--- a/tools/test/topos/geant.py
+++ b/tools/test/topos/geant.py
@@ -15,6 +15,11 @@
 class GeantMplsTopo( Topo ):
     "Internet Topology Zoo Specimen."
 
+    def addSwitch( self, name, **opts ):
+        kwargs = { 'protocols' : 'OpenFlow13' }
+        kwargs.update( opts )
+        return super(GeantMplsTopo, self).addSwitch( name, **kwargs )
+
     def __init__( self ):
         "Create a topology."
 
@@ -23,14 +28,14 @@
 
         # add nodes, switches first...
         ATH = self.addSwitch( 's1' )
-        LIS = self.addSwitch( 's2', protocols='OpenFlow13' )
+        LIS = self.addSwitch( 's2' )
         LON = self.addSwitch( 's3' )
         BRU = self.addSwitch( 's4' )
         PAR = self.addSwitch( 's5' )
         DUB = self.addSwitch( 's6' )
         MAD = self.addSwitch( 's7' )
         GEN = self.addSwitch( 's8' )
-        MIL = self.addSwitch( 's9', protocols='OpenFlow13' )
+        MIL = self.addSwitch( 's9' )
         SOF = self.addSwitch( 's10' )
         BUC = self.addSwitch( 's11' )
         VIE = self.addSwitch( 's12' )
@@ -43,7 +48,7 @@
         PRA = self.addSwitch( 's19' )
         BRA = self.addSwitch( 's20' )
         ZAG = self.addSwitch( 's21' )
-        LJU = self.addSwitch( 's22', protocols='OpenFlow13' )
+        LJU = self.addSwitch( 's22' )
         BUD = self.addSwitch( 's23' )
         MLT = self.addSwitch( 's24' )
         LUX = self.addSwitch( 's25' )
@@ -122,62 +127,62 @@
         self.addLink( HEL , HEL_host )
 
         # add edges between switches
-        self.addLink( ATH , MIL, bw=10 )
-        self.addLink( MIL , ATH, bw=10 )
-        self.addLink( MIL , VIE, bw=10 )
-        self.addLink( MIL , MAR, bw=10 )
-        self.addLink( MIL , GEN, bw=10 )
-        self.addLink( GEN , MIL, bw=10 )
-        self.addLink( MIL , MLT, bw=10 )
-        self.addLink( GEN , FRA, bw=10 )
-        self.addLink( FRA , GEN, bw=10 )
-        self.addLink( GEN , PAR, bw=10 )
-        self.addLink( PAR , GEN, bw=10 )
-        self.addLink( GEN , PAR, bw=10 )
-        self.addLink( FRA , POZ, bw=10 )
-        self.addLink( GEN , MAR, bw=10 )
-        self.addLink( MAR , MAD, bw=10 )
-        self.addLink( MAD , PAR, bw=10 )
-        self.addLink( MAD , LIS, bw=10 )
-        self.addLink( LIS , LON, bw=10 )
-        self.addLink( LON , LIS, bw=10 )
-        self.addLink( LON , PAR, bw=10 )
-        self.addLink( LON , DUB, bw=10 )
-        self.addLink( DUB , LON, bw=10 )
-        self.addLink( LON , BRU, bw=10 )
-        self.addLink( BRU , AMS, bw=10 )
-        self.addLink( AMS , LUX, bw=10 )
-        self.addLink( LUX , FRA, bw=10 )
-        self.addLink( AMS , HAM, bw=10 )
-        self.addLink( HAM , FRA, bw=10 )
-        self.addLink( HAM , COP, bw=10 )
-        self.addLink( COP , AMS, bw=10 )
-        self.addLink( FRA , POZ, bw=10 )
-        self.addLink( FRA , PRA, bw=10 )
-        self.addLink( FRA , BUD, bw=10 )
-        self.addLink( FRA , VIE, bw=10 )
-        self.addLink( POZ , PRA, bw=10 )
-        self.addLink( POZ , KAU, bw=10 )
-        self.addLink( KAU , RIG, bw=10 )
-        self.addLink( ZAG , VIE, bw=10 )
-        self.addLink( ZAG , BUD, bw=10 )
-        self.addLink( BUD , PRA, bw=10 )
-        self.addLink( BUD , BRA, bw=10 )
-        self.addLink( BUD , BUC, bw=10 )
-        self.addLink( BUD , SOF, bw=10 )
-        self.addLink( BUD , LJU, bw=10 )
-        self.addLink( BUC , SOF, bw=10 )
-        self.addLink( BUC , VIE, bw=10 )
-        self.addLink( VIE , BRA, bw=10 )
-        self.addLink( RIG , TLN, bw=10 )
-        self.addLink( TLN , HAM, bw=10 )
-        self.addLink( OSL , STO, bw=10 )
-        self.addLink( STO , HEL, bw=10 )
-        self.addLink( STO , COP, bw=10 )
-        self.addLink( OSL , COP, bw=10 )
-        self.addLink( TLN , HEL, bw=10 )
+        self.addLink( ATH , MIL )
+        self.addLink( MIL , ATH )
+        self.addLink( MIL , VIE )
+        self.addLink( MIL , MAR )
+        self.addLink( MIL , GEN )
+        self.addLink( GEN , MIL )
+        self.addLink( MIL , MLT )
+        self.addLink( GEN , FRA )
+        self.addLink( FRA , GEN )
+        self.addLink( GEN , PAR )
+        self.addLink( PAR , GEN )
+        self.addLink( GEN , PAR )
+        self.addLink( FRA , POZ )
+        self.addLink( GEN , MAR )
+        self.addLink( MAR , MAD )
+        self.addLink( MAD , PAR )
+        self.addLink( MAD , LIS )
+        self.addLink( LIS , LON )
+        self.addLink( LON , LIS )
+        self.addLink( LON , PAR )
+        self.addLink( LON , DUB )
+        self.addLink( DUB , LON )
+        self.addLink( LON , BRU )
+        self.addLink( BRU , AMS )
+        self.addLink( AMS , LUX )
+        self.addLink( LUX , FRA )
+        self.addLink( AMS , HAM )
+        self.addLink( HAM , FRA )
+        self.addLink( HAM , COP )
+        self.addLink( COP , AMS )
+        self.addLink( FRA , POZ )
+        self.addLink( FRA , PRA )
+        self.addLink( FRA , BUD )
+        self.addLink( FRA , VIE )
+        self.addLink( POZ , PRA )
+        self.addLink( POZ , KAU )
+        self.addLink( KAU , RIG )
+        self.addLink( ZAG , VIE )
+        self.addLink( ZAG , BUD )
+        self.addLink( BUD , PRA )
+        self.addLink( BUD , BRA )
+        self.addLink( BUD , BUC )
+        self.addLink( BUD , SOF )
+        self.addLink( BUD , LJU )
+        self.addLink( BUC , SOF )
+        self.addLink( BUC , VIE )
+        self.addLink( VIE , BRA )
+        self.addLink( RIG , TLN )
+        self.addLink( TLN , HAM )
+        self.addLink( OSL , STO )
+        self.addLink( STO , HEL )
+        self.addLink( STO , COP )
+        self.addLink( OSL , COP )
+        self.addLink( TLN , HEL )
 
-topos = { 'att': ( lambda: GeantMplsTopo() ) }
+topos = { 'geant': ( lambda: GeantMplsTopo() ) }
 
 if __name__ == '__main__':
     from onosnet import run
diff --git a/utils/junit/pom.xml b/utils/junit/pom.xml
index e0846ed..c30eaf2 100644
--- a/utils/junit/pom.xml
+++ b/utils/junit/pom.xml
@@ -53,4 +53,20 @@
         </dependency>
     </dependencies>
 
+    <build>
+        <plugins>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <compilerArgs>
+                      <arg>-Xep:BetaApi:OFF</arg>
+                    </compilerArgs>
+                </configuration>
+            </plugin>
+
+        </plugins>
+    </build>
+
 </project>
diff --git a/utils/misc/pom.xml b/utils/misc/pom.xml
index 653ed4a..1bfc038 100644
--- a/utils/misc/pom.xml
+++ b/utils/misc/pom.xml
@@ -31,7 +31,6 @@
     <description>Miscellaneous ON.Lab utilities</description>
 
     <properties>
-        <metrics.version>3.1.2</metrics.version>
     </properties>
 
     <dependencies>
@@ -77,12 +76,10 @@
         <dependency>
             <groupId>io.dropwizard.metrics</groupId>
             <artifactId>metrics-core</artifactId>
-            <version>${metrics.version}</version>
         </dependency>
         <dependency>
             <groupId>io.dropwizard.metrics</groupId>
             <artifactId>metrics-json</artifactId>
-            <version>${metrics.version}</version>
         </dependency>
         <dependency>
             <groupId>org.apache.felix</groupId>
diff --git a/utils/misc/src/main/java/org/onlab/packet/DHCP6.java b/utils/misc/src/main/java/org/onlab/packet/DHCP6.java
index 054552c..8dbd1dc 100644
--- a/utils/misc/src/main/java/org/onlab/packet/DHCP6.java
+++ b/utils/misc/src/main/java/org/onlab/packet/DHCP6.java
@@ -79,6 +79,70 @@
         public byte value() {
             return this.value;
         }
+        public static MsgType getType(final int value) {
+            switch (value) {
+                case 1:
+                    return SOLICIT;
+                case 2:
+                    return ADVERTISE;
+                case 3:
+                    return REQUEST;
+                case 4:
+                    return CONFIRM;
+                case 5:
+                    return RENEW;
+                case 6:
+                    return REBIND;
+                case 7:
+                    return REPLY;
+                case 8:
+                    return RELEASE;
+                case 9:
+                    return DECLINE;
+                case 10:
+                    return RECONFIGURE;
+                case 11:
+                    return INFORMATION_REQUEST;
+                case 12:
+                    return RELAY_FORW;
+                case 13:
+                    return RELAY_REPL;
+                default:
+                    return null;
+            }
+        }
+        public static String  getMsgTypeStr(final MsgType msgType) {
+            switch (msgType) {
+                case SOLICIT:
+                    return "SOLICIT";
+                case ADVERTISE:
+                    return "ADVERTISE";
+                case REQUEST:
+                    return "REQUEST";
+                case CONFIRM:
+                    return "CONFIRM";
+                case RENEW:
+                    return "RENEW";
+                case REBIND:
+                    return "REBIND";
+                case REPLY:
+                    return "REPLY";
+                case RELEASE:
+                    return "RELEASE";
+                case DECLINE:
+                    return "DECLINE";
+                case RECONFIGURE:
+                    return "RECONFIGURE";
+                case INFORMATION_REQUEST:
+                    return "INFORMATION_REQUEST";
+                case RELAY_FORW:
+                    return "RELAY_FORW";
+                case RELAY_REPL:
+                    return "RELAY_REPL";
+                default:
+                    return "NULL";
+            }
+        }
     }
 
     /**
diff --git a/utils/misc/src/main/java/org/onlab/packet/DHCPPacketType.java b/utils/misc/src/main/java/org/onlab/packet/DHCPPacketType.java
index 2dad848..c88b907 100644
--- a/utils/misc/src/main/java/org/onlab/packet/DHCPPacketType.java
+++ b/utils/misc/src/main/java/org/onlab/packet/DHCPPacketType.java
@@ -80,7 +80,7 @@
             break;
         }
 
-        return null;
+        return "";
     }
 
     public static DHCPPacketType getType(final int value) {
diff --git a/utils/misc/src/main/java/org/onlab/packet/RIPngEntry.java b/utils/misc/src/main/java/org/onlab/packet/RIPngEntry.java
index acc500c..cfe3b62 100644
--- a/utils/misc/src/main/java/org/onlab/packet/RIPngEntry.java
+++ b/utils/misc/src/main/java/org/onlab/packet/RIPngEntry.java
@@ -33,13 +33,13 @@
     public static final int OPT_CODE_LEN = 1;
     public static final int ENTRY_LEN = 20;
     public static final byte INFINITY_METRIC = 16;
-    public static final byte NEXTHOP_METRIC =  -128; // actually it is 0xFF
+    public static final int NEXTHOP_METRIC =  255; // actually it is 0xFF
 
     private final Logger log = getLogger(getClass());
     protected byte[] prefix; // 16 bytes
     protected short routeTag;
-    protected byte prefixLen;
-    protected byte metric;
+    protected int prefixLen;
+    protected int metric;
 
     @Override
     public byte[] serialize() {
@@ -47,8 +47,8 @@
         byteBuffer = ByteBuffer.allocate(ENTRY_LEN);
         byteBuffer.put(prefix);
         byteBuffer.putShort(routeTag);
-        byteBuffer.put(prefixLen);
-        byteBuffer.put(metric);
+        byteBuffer.put((byte) prefixLen);
+        byteBuffer.put((byte) metric);
         return byteBuffer.array();
     }
 
@@ -76,8 +76,8 @@
             ripngEntry.prefix = new byte[IpAddress.INET6_BYTE_LENGTH];
             bb.get(ripngEntry.prefix);
             ripngEntry.routeTag = bb.getShort();
-            ripngEntry.prefixLen = bb.get();
-            ripngEntry.metric = bb.get();
+            ripngEntry.prefixLen = 0xFF & bb.get();
+            ripngEntry.metric = 0xFF & bb.get();
             return ripngEntry;
         };
     }
@@ -148,7 +148,7 @@
     /**
      * @return the prefix length
      */
-    public byte getPrefixLen() {
+    public int getPrefixLen() {
         return this.prefixLen;
     }
 
@@ -156,7 +156,7 @@
      * @param prefixlen the prefix length to set
      * @return this
      */
-    public RIPngEntry setPrefixLen(final byte prefixlen) {
+    public RIPngEntry setPrefixLen(final int prefixlen) {
         this.prefixLen = prefixlen;
         return this;
     }
@@ -164,7 +164,7 @@
     /**
      * @return the metric
      */
-    public byte getMetric() {
+    public int getMetric() {
         return this.metric;
     }
 
@@ -172,7 +172,7 @@
      * @param metric the route metric to set
      * @return this
      */
-    public RIPngEntry setMetric(final byte metric) {
+    public RIPngEntry setMetric(final int metric) {
         this.metric = metric;
         return this;
     }
@@ -184,8 +184,7 @@
      */
     @Override
     public String toString() {
-        return "RIPngEntry [prefix=" + Arrays.toString(this.prefix)
-                + ", route tag=" + this.routeTag
+        return "RIPngEntry [prefix=" + Arrays.toString(this.prefix) + ", route tag=" + this.routeTag
                 + ", prefix length=" + this.prefixLen
                 + ", metric = " + this.metric + "]";
     }
diff --git a/utils/misc/src/main/java/org/onlab/util/Tools.java b/utils/misc/src/main/java/org/onlab/util/Tools.java
index 0989010..bf9bea0 100644
--- a/utils/misc/src/main/java/org/onlab/util/Tools.java
+++ b/utils/misc/src/main/java/org/onlab/util/Tools.java
@@ -162,7 +162,7 @@
      * @param collection collection to test
      * @return true if null or empty; false otherwise
      */
-    public static boolean isNullOrEmpty(Collection collection) {
+    public static boolean isNullOrEmpty(Collection<?> collection) {
         return collection == null || collection.isEmpty();
     }
 
diff --git a/utils/rest/src/main/java/org/onlab/rest/BaseResource.java b/utils/rest/src/main/java/org/onlab/rest/BaseResource.java
index 093220f..44d7c99 100644
--- a/utils/rest/src/main/java/org/onlab/rest/BaseResource.java
+++ b/utils/rest/src/main/java/org/onlab/rest/BaseResource.java
@@ -28,18 +28,6 @@
     private static ServiceDirectory services = new DefaultServiceDirectory();
 
     /**
-     * Sets alternate service directory to be used for lookups.
-     * <p>
-     * Intended to ease unit testing and not intended for use in production.
-     * </p>
-     *
-     * @param serviceDirectory alternate service directory
-     */
-    public static void setServiceDirectory(ServiceDirectory serviceDirectory) {
-        services = serviceDirectory;
-    }
-
-    /**
      * Returns reference to the specified service implementation.
      *
      * @param service service class
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/FlowObjectiveWebResource.java b/web/api/src/main/java/org/onosproject/rest/resources/FlowObjectiveWebResource.java
index 4f186ef..e6f7e9c 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/FlowObjectiveWebResource.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/FlowObjectiveWebResource.java
@@ -75,22 +75,22 @@
         try {
             UriBuilder locationBuilder = null;
             ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
-            if (validateDeviceId(deviceId, jsonTree)) {
+            validateDeviceId(deviceId, jsonTree);
 
-                if (appId != null) {
-                    jsonTree.put("appId", appId);
-                }
-
-                DeviceId did = DeviceId.deviceId(deviceId);
-                FilteringObjective filteringObjective =
-                        codec(FilteringObjective.class).decode(jsonTree, this);
-                flowObjectiveService.filter(did, filteringObjective);
-                locationBuilder = uriInfo.getBaseUriBuilder()
-                        .path("flowobjectives")
-                        .path(did.toString())
-                        .path("filter")
-                        .path(Integer.toString(filteringObjective.id()));
+            if (appId != null) {
+                jsonTree.put("appId", appId);
             }
+
+            DeviceId did = DeviceId.deviceId(deviceId);
+            FilteringObjective filteringObjective =
+                    codec(FilteringObjective.class).decode(jsonTree, this);
+            flowObjectiveService.filter(did, filteringObjective);
+            locationBuilder = uriInfo.getBaseUriBuilder()
+                    .path("flowobjectives")
+                    .path(did.toString())
+                    .path("filter")
+                    .path(Integer.toString(filteringObjective.id()));
+
             return Response
                     .created(locationBuilder.build())
                     .build();
@@ -119,22 +119,22 @@
         try {
             UriBuilder locationBuilder = null;
             ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
-            if (validateDeviceId(deviceId, jsonTree)) {
+            validateDeviceId(deviceId, jsonTree);
 
-                if (appId != null) {
-                    jsonTree.put("appId", appId);
-                }
-
-                DeviceId did = DeviceId.deviceId(deviceId);
-                ForwardingObjective forwardingObjective =
-                        codec(ForwardingObjective.class).decode(jsonTree, this);
-                flowObjectiveService.forward(did, forwardingObjective);
-                locationBuilder = uriInfo.getBaseUriBuilder()
-                        .path("flowobjectives")
-                        .path(did.toString())
-                        .path("forward")
-                        .path(Integer.toString(forwardingObjective.id()));
+            if (appId != null) {
+                jsonTree.put("appId", appId);
             }
+
+            DeviceId did = DeviceId.deviceId(deviceId);
+            ForwardingObjective forwardingObjective =
+                    codec(ForwardingObjective.class).decode(jsonTree, this);
+            flowObjectiveService.forward(did, forwardingObjective);
+            locationBuilder = uriInfo.getBaseUriBuilder()
+                    .path("flowobjectives")
+                    .path(did.toString())
+                    .path("forward")
+                    .path(Integer.toString(forwardingObjective.id()));
+
             return Response
                     .created(locationBuilder.build())
                     .build();
@@ -163,22 +163,22 @@
         try {
             UriBuilder locationBuilder = null;
             ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
-            if (validateDeviceId(deviceId, jsonTree)) {
+            validateDeviceId(deviceId, jsonTree);
 
-                if (appId != null) {
-                    jsonTree.put("appId", appId);
-                }
-
-                DeviceId did = DeviceId.deviceId(deviceId);
-                NextObjective nextObjective =
-                        codec(NextObjective.class).decode(jsonTree, this);
-                flowObjectiveService.next(did, nextObjective);
-                locationBuilder = uriInfo.getBaseUriBuilder()
-                        .path("flowobjectives")
-                        .path(did.toString())
-                        .path("next")
-                        .path(Integer.toString(nextObjective.id()));
+            if (appId != null) {
+                jsonTree.put("appId", appId);
             }
+
+            DeviceId did = DeviceId.deviceId(deviceId);
+            NextObjective nextObjective =
+                    codec(NextObjective.class).decode(jsonTree, this);
+            flowObjectiveService.next(did, nextObjective);
+            locationBuilder = uriInfo.getBaseUriBuilder()
+                    .path("flowobjectives")
+                    .path(did.toString())
+                    .path("next")
+                    .path(Integer.toString(nextObjective.id()));
+
             return Response
                     .created(locationBuilder.build())
                     .build();
@@ -234,15 +234,14 @@
      *
      * @param deviceId device identifier
      * @param node     object node
-     * @return validity
+     * @throws IllegalArgumentException if the device id is invalid
      */
-    private boolean validateDeviceId(String deviceId, ObjectNode node) {
+    private void validateDeviceId(String deviceId, ObjectNode node) {
         JsonNode specifiedDeviceId = node.get("deviceId");
 
         if (specifiedDeviceId != null &&
                 !specifiedDeviceId.asText().equals(deviceId)) {
             throw new IllegalArgumentException(DEVICE_INVALID);
         }
-        return true;
     }
 }
diff --git a/web/api/src/test/java/org/onosproject/rest/resources/ApplicationsResourceTest.java b/web/api/src/test/java/org/onosproject/rest/resources/ApplicationsResourceTest.java
index 48cbd2d..3de04ba 100644
--- a/web/api/src/test/java/org/onosproject/rest/resources/ApplicationsResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/resources/ApplicationsResourceTest.java
@@ -27,7 +27,6 @@
 import org.junit.Test;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
-import org.onlab.rest.BaseResource;
 import org.onosproject.app.ApplicationAdminService;
 import org.onosproject.app.ApplicationService;
 import org.onosproject.app.ApplicationState;
@@ -295,7 +294,7 @@
                         .add(CoreService.class, coreService)
                         .add(CodecService.class, codecService);
 
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
     }
 
     /**
diff --git a/web/api/src/test/java/org/onosproject/rest/resources/ComponentConfigWebResourceTest.java b/web/api/src/test/java/org/onosproject/rest/resources/ComponentConfigWebResourceTest.java
index d5621c9..6ff02e3 100644
--- a/web/api/src/test/java/org/onosproject/rest/resources/ComponentConfigWebResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/resources/ComponentConfigWebResourceTest.java
@@ -20,7 +20,6 @@
 import org.junit.Test;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
-import org.onlab.rest.BaseResource;
 import org.onosproject.cfg.ComponentConfigAdapter;
 import org.onosproject.cfg.ComponentConfigService;
 import org.onosproject.cfg.ConfigProperty;
@@ -51,7 +50,7 @@
         ServiceDirectory testDirectory =
                 new TestServiceDirectory()
                         .add(ComponentConfigService.class, service);
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
     }
 
     @Test
diff --git a/web/api/src/test/java/org/onosproject/rest/resources/DeviceKeyWebResourceTest.java b/web/api/src/test/java/org/onosproject/rest/resources/DeviceKeyWebResourceTest.java
index 8ae7b3d..7ecaf38 100644
--- a/web/api/src/test/java/org/onosproject/rest/resources/DeviceKeyWebResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/resources/DeviceKeyWebResourceTest.java
@@ -26,7 +26,6 @@
 import org.junit.Test;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
-import org.onlab.rest.BaseResource;
 import org.onosproject.codec.CodecService;
 import org.onosproject.codec.impl.CodecManager;
 import org.onosproject.net.key.DeviceKey;
@@ -109,7 +108,7 @@
                         .add(DeviceKeyAdminService.class, mockDeviceKeyAdminService)
                         .add(CodecService.class, codecService);
 
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
     }
 
     /**
diff --git a/web/api/src/test/java/org/onosproject/rest/resources/DevicesResourceTest.java b/web/api/src/test/java/org/onosproject/rest/resources/DevicesResourceTest.java
index 169cb11..75d33a8 100644
--- a/web/api/src/test/java/org/onosproject/rest/resources/DevicesResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/resources/DevicesResourceTest.java
@@ -27,7 +27,6 @@
 import org.junit.Test;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
-import org.onlab.rest.BaseResource;
 import org.onosproject.codec.CodecService;
 import org.onosproject.codec.impl.CodecManager;
 import org.onosproject.net.DefaultPort;
@@ -36,16 +35,15 @@
 import org.onosproject.net.MastershipRole;
 import org.onosproject.net.Port;
 import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.driver.DriverService;
 import org.onosproject.net.driver.DefaultDriver;
-import org.onosproject.net.driver.TestBehaviourImpl;
+import org.onosproject.net.driver.DriverService;
 import org.onosproject.net.driver.TestBehaviour;
+import org.onosproject.net.driver.TestBehaviourImpl;
 import org.onosproject.net.driver.TestBehaviourTwo;
 import org.onosproject.net.driver.TestBehaviourTwoImpl;
 
 import javax.ws.rs.NotFoundException;
 import javax.ws.rs.client.WebTarget;
-
 import java.util.ArrayList;
 import java.util.List;
 
@@ -247,7 +245,7 @@
                         .add(DriverService.class, mockDriverService)
                         .add(CodecService.class, codecService);
 
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
     }
 
     /**
diff --git a/web/api/src/test/java/org/onosproject/rest/resources/FlowObjectiveResourceTest.java b/web/api/src/test/java/org/onosproject/rest/resources/FlowObjectiveResourceTest.java
index 6d858d5..eb99cff 100644
--- a/web/api/src/test/java/org/onosproject/rest/resources/FlowObjectiveResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/resources/FlowObjectiveResourceTest.java
@@ -23,7 +23,6 @@
 import org.junit.Test;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
-import org.onlab.rest.BaseResource;
 import org.onosproject.codec.CodecService;
 import org.onosproject.codec.impl.CodecManager;
 import org.onosproject.core.CoreService;
@@ -79,7 +78,7 @@
                         .add(CodecService.class, codecService)
                         .add(CoreService.class, mockCoreService);
 
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
     }
 
     /**
diff --git a/web/api/src/test/java/org/onosproject/rest/resources/FlowsResourceTest.java b/web/api/src/test/java/org/onosproject/rest/resources/FlowsResourceTest.java
index 6e8255c..72e9a00 100644
--- a/web/api/src/test/java/org/onosproject/rest/resources/FlowsResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/resources/FlowsResourceTest.java
@@ -15,19 +15,10 @@
  */
 package org.onosproject.rest.resources;
 
-import java.io.InputStream;
-import java.net.HttpURLConnection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
-import javax.ws.rs.NotFoundException;
-import javax.ws.rs.client.Entity;
-import javax.ws.rs.client.WebTarget;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-
+import com.eclipsesource.json.Json;
+import com.eclipsesource.json.JsonArray;
+import com.eclipsesource.json.JsonObject;
+import com.google.common.collect.ImmutableSet;
 import org.hamcrest.Description;
 import org.hamcrest.Matchers;
 import org.hamcrest.TypeSafeMatcher;
@@ -37,7 +28,6 @@
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
 import org.onlab.packet.MacAddress;
-import org.onlab.rest.BaseResource;
 import org.onosproject.app.ApplicationService;
 import org.onosproject.codec.CodecService;
 import org.onosproject.codec.impl.CodecManager;
@@ -61,10 +51,17 @@
 import org.onosproject.net.flow.criteria.Criterion;
 import org.onosproject.net.flow.instructions.Instruction;
 
-import com.eclipsesource.json.Json;
-import com.eclipsesource.json.JsonArray;
-import com.eclipsesource.json.JsonObject;
-import com.google.common.collect.ImmutableSet;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
 
 import static java.util.concurrent.TimeUnit.SECONDS;
 import static org.easymock.EasyMock.anyObject;
@@ -276,7 +273,7 @@
                         .add(CoreService.class, mockCoreService)
                         .add(ApplicationService.class, mockApplicationService);
 
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
     }
 
     /**
diff --git a/web/api/src/test/java/org/onosproject/rest/resources/GroupsResourceTest.java b/web/api/src/test/java/org/onosproject/rest/resources/GroupsResourceTest.java
index 1efbd33..ab4cf9d 100644
--- a/web/api/src/test/java/org/onosproject/rest/resources/GroupsResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/resources/GroupsResourceTest.java
@@ -28,7 +28,6 @@
 import org.junit.Test;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
-import org.onlab.rest.BaseResource;
 import org.onosproject.codec.CodecService;
 import org.onosproject.codec.impl.CodecManager;
 import org.onosproject.codec.impl.GroupCodec;
@@ -241,7 +240,7 @@
                         .add(CodecService.class, codecService)
                         .add(CoreService.class, mockCoreService);
 
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
     }
 
     /**
diff --git a/web/api/src/test/java/org/onosproject/rest/resources/HostResourceTest.java b/web/api/src/test/java/org/onosproject/rest/resources/HostResourceTest.java
index 99fa074..4bb9523 100644
--- a/web/api/src/test/java/org/onosproject/rest/resources/HostResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/resources/HostResourceTest.java
@@ -31,7 +31,6 @@
 import org.onlab.osgi.TestServiceDirectory;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
-import org.onlab.rest.BaseResource;
 import org.onosproject.codec.CodecService;
 import org.onosproject.codec.impl.CodecManager;
 import org.onosproject.net.DefaultHost;
@@ -99,7 +98,7 @@
                         .add(HostAdminService.class, mockHostService)
                         .add(CodecService.class, codecService)
                         .add(HostProviderRegistry.class, mockHostProviderRegistry);
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
     }
 
     /**
diff --git a/web/api/src/test/java/org/onosproject/rest/resources/IntentsResourceTest.java b/web/api/src/test/java/org/onosproject/rest/resources/IntentsResourceTest.java
index b9b5dbd..e57af12 100644
--- a/web/api/src/test/java/org/onosproject/rest/resources/IntentsResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/resources/IntentsResourceTest.java
@@ -29,7 +29,6 @@
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
 import org.onlab.packet.MacAddress;
-import org.onlab.rest.BaseResource;
 import org.onosproject.codec.CodecService;
 import org.onosproject.codec.impl.CodecManager;
 import org.onosproject.codec.impl.MockCodecContext;
@@ -746,7 +745,7 @@
                         .add(CodecService.class, codecService)
                         .add(CoreService.class, mockCoreService);
 
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
 
         MockIdGenerator.cleanBind();
     }
diff --git a/web/api/src/test/java/org/onosproject/rest/resources/LinksResourceTest.java b/web/api/src/test/java/org/onosproject/rest/resources/LinksResourceTest.java
index 1063002..e26bcee 100644
--- a/web/api/src/test/java/org/onosproject/rest/resources/LinksResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/resources/LinksResourceTest.java
@@ -27,7 +27,6 @@
 import org.junit.Test;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
-import org.onlab.rest.BaseResource;
 import org.onosproject.codec.CodecService;
 import org.onosproject.codec.impl.CodecManager;
 import org.onosproject.net.ConnectPoint;
@@ -162,7 +161,7 @@
                         .add(LinkService.class, mockLinkService)
                         .add(CodecService.class, codecService);
 
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
     }
 
     /**
diff --git a/web/api/src/test/java/org/onosproject/rest/resources/MastershipResourceTest.java b/web/api/src/test/java/org/onosproject/rest/resources/MastershipResourceTest.java
index 724075c..76d9a8d 100644
--- a/web/api/src/test/java/org/onosproject/rest/resources/MastershipResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/resources/MastershipResourceTest.java
@@ -28,7 +28,6 @@
 import org.junit.Test;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
-import org.onlab.rest.BaseResource;
 import org.onosproject.cluster.NodeId;
 import org.onosproject.cluster.RoleInfo;
 import org.onosproject.codec.CodecService;
@@ -170,7 +169,7 @@
                         .add(DeviceService.class, mockDeviceService)
                         .add(CodecService.class, codecService);
 
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
     }
 
     /**
diff --git a/web/api/src/test/java/org/onosproject/rest/resources/MetersResourceTest.java b/web/api/src/test/java/org/onosproject/rest/resources/MetersResourceTest.java
index e225cdd..235ca0f 100644
--- a/web/api/src/test/java/org/onosproject/rest/resources/MetersResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/resources/MetersResourceTest.java
@@ -28,7 +28,6 @@
 import org.junit.Test;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
-import org.onlab.rest.BaseResource;
 import org.onosproject.codec.CodecService;
 import org.onosproject.codec.impl.CodecManager;
 import org.onosproject.codec.impl.MeterCodec;
@@ -239,7 +238,7 @@
                         .add(CodecService.class, codecService)
                         .add(CoreService.class, mockCoreService);
 
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
     }
 
     /**
diff --git a/web/api/src/test/java/org/onosproject/rest/resources/MetricsResourceTest.java b/web/api/src/test/java/org/onosproject/rest/resources/MetricsResourceTest.java
index feac73f..75e7f02 100644
--- a/web/api/src/test/java/org/onosproject/rest/resources/MetricsResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/resources/MetricsResourceTest.java
@@ -31,7 +31,6 @@
 import org.onlab.metrics.MetricsService;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
-import org.onlab.rest.BaseResource;
 import org.onosproject.codec.CodecService;
 import org.onosproject.codec.impl.CodecManager;
 
@@ -68,7 +67,7 @@
                 new TestServiceDirectory()
                         .add(MetricsService.class, mockMetricsService)
                         .add(CodecService.class, codecService);
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
     }
 
     /**
diff --git a/web/api/src/test/java/org/onosproject/rest/resources/MulticastRouteResourceTest.java b/web/api/src/test/java/org/onosproject/rest/resources/MulticastRouteResourceTest.java
index 33af8f4..64c13b4 100644
--- a/web/api/src/test/java/org/onosproject/rest/resources/MulticastRouteResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/resources/MulticastRouteResourceTest.java
@@ -27,7 +27,6 @@
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
 import org.onlab.packet.IpAddress;
-import org.onlab.rest.BaseResource;
 import org.onosproject.codec.CodecService;
 import org.onosproject.codec.impl.CodecManager;
 import org.onosproject.net.mcast.McastRoute;
@@ -85,7 +84,7 @@
                 new TestServiceDirectory()
                         .add(MulticastRouteService.class, mockMulticastRouteService)
                         .add(CodecService.class, codecService);
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
     }
 
     /**
diff --git a/web/api/src/test/java/org/onosproject/rest/resources/NetworkConfigWebResourceTest.java b/web/api/src/test/java/org/onosproject/rest/resources/NetworkConfigWebResourceTest.java
index b02e1a6..b2fbf60 100644
--- a/web/api/src/test/java/org/onosproject/rest/resources/NetworkConfigWebResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/resources/NetworkConfigWebResourceTest.java
@@ -15,24 +15,18 @@
  */
 package org.onosproject.rest.resources;
 
-import java.io.InputStream;
-import java.net.HttpURLConnection;
-import java.util.HashSet;
-import java.util.Set;
-
-import javax.ws.rs.NotFoundException;
-import javax.ws.rs.client.Entity;
-import javax.ws.rs.client.WebTarget;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-
+import com.eclipsesource.json.Json;
+import com.eclipsesource.json.JsonObject;
+import com.eclipsesource.json.JsonValue;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.ImmutableSet;
 import org.eclipse.jetty.http.HttpStatus;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
-import org.onlab.rest.BaseResource;
 import org.onosproject.net.DefaultDevice;
 import org.onosproject.net.Device;
 import org.onosproject.net.Link;
@@ -41,12 +35,15 @@
 import org.onosproject.net.config.NetworkConfigServiceAdapter;
 import org.onosproject.net.config.SubjectFactory;
 
-import com.eclipsesource.json.Json;
-import com.eclipsesource.json.JsonObject;
-import com.eclipsesource.json.JsonValue;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.common.collect.ImmutableSet;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.util.HashSet;
+import java.util.Set;
 
 import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.replay;
@@ -204,7 +201,7 @@
         ServiceDirectory testDirectory =
                 new TestServiceDirectory()
                         .add(NetworkConfigService.class, mockNetworkConfigService);
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
     }
 
     /**
diff --git a/web/api/src/test/java/org/onosproject/rest/resources/PathsResourceTest.java b/web/api/src/test/java/org/onosproject/rest/resources/PathsResourceTest.java
index 4d415f5..97d04bb 100644
--- a/web/api/src/test/java/org/onosproject/rest/resources/PathsResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/resources/PathsResourceTest.java
@@ -26,7 +26,6 @@
 import org.junit.Test;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
-import org.onlab.rest.BaseResource;
 import org.onosproject.codec.CodecService;
 import org.onosproject.codec.impl.CodecManager;
 import org.onosproject.net.ElementId;
@@ -143,7 +142,7 @@
                         .add(PathService.class, mockPathService)
                         .add(CodecService.class, codecService);
 
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
     }
 
     /**
diff --git a/web/api/src/test/java/org/onosproject/rest/resources/RegionsResourceTest.java b/web/api/src/test/java/org/onosproject/rest/resources/RegionsResourceTest.java
index 3a0300d..69894e4 100644
--- a/web/api/src/test/java/org/onosproject/rest/resources/RegionsResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/resources/RegionsResourceTest.java
@@ -28,7 +28,6 @@
 import org.junit.Test;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
-import org.onlab.rest.BaseResource;
 import org.onosproject.cluster.NodeId;
 import org.onosproject.codec.CodecService;
 import org.onosproject.codec.impl.CodecManager;
@@ -142,7 +141,7 @@
                 .add(RegionService.class, mockRegionService)
                 .add(RegionAdminService.class, mockRegionAdminService)
                 .add(CodecService.class, codecService);
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
     }
 
     /**
diff --git a/web/api/src/test/java/org/onosproject/rest/resources/ResourceTest.java b/web/api/src/test/java/org/onosproject/rest/resources/ResourceTest.java
index f032cd9..24036f6 100644
--- a/web/api/src/test/java/org/onosproject/rest/resources/ResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/resources/ResourceTest.java
@@ -21,6 +21,9 @@
 import org.glassfish.jersey.test.jetty.JettyTestContainerFactory;
 import org.glassfish.jersey.test.spi.TestContainerException;
 import org.glassfish.jersey.test.spi.TestContainerFactory;
+import org.onlab.junit.TestUtils;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.rest.BaseResource;
 
 /**
  * Base class for REST API tests.
@@ -58,4 +61,13 @@
     protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
         return new JettyTestContainerFactory();
     }
+
+    /**
+     * Sets up the test services directory in the base resource environment.
+     *
+     * @param testDirectory new test directory
+     */
+    protected void setServiceDirectory(ServiceDirectory testDirectory) {
+        TestUtils.setField(BaseResource.class, "services", testDirectory);
+    }
 }
diff --git a/web/api/src/test/java/org/onosproject/rest/resources/StatisticsResourceTest.java b/web/api/src/test/java/org/onosproject/rest/resources/StatisticsResourceTest.java
index 66b6eb0..a1bbccb 100644
--- a/web/api/src/test/java/org/onosproject/rest/resources/StatisticsResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/resources/StatisticsResourceTest.java
@@ -24,7 +24,6 @@
 import org.junit.Test;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
-import org.onlab.rest.BaseResource;
 import org.onosproject.codec.CodecService;
 import org.onosproject.codec.impl.CodecManager;
 import org.onosproject.net.Link;
@@ -92,7 +91,7 @@
                         .add(StatisticService.class, mockStatisticService)
                         .add(CodecService.class, codecService);
 
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
     }
 
     /**
diff --git a/web/api/src/test/java/org/onosproject/rest/resources/TenantWebResourceTest.java b/web/api/src/test/java/org/onosproject/rest/resources/TenantWebResourceTest.java
index 69bd45b..781cfae 100644
--- a/web/api/src/test/java/org/onosproject/rest/resources/TenantWebResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/resources/TenantWebResourceTest.java
@@ -28,7 +28,6 @@
 import org.junit.Test;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
-import org.onlab.rest.BaseResource;
 import org.onosproject.codec.CodecService;
 import org.onosproject.codec.impl.CodecManager;
 import org.onosproject.incubator.net.virtual.TenantId;
@@ -44,9 +43,19 @@
 import java.net.HttpURLConnection;
 import java.util.HashSet;
 
-import static org.easymock.EasyMock.*;
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
 
 /**
  * Unit tests for tenant REST APIs.
@@ -77,7 +86,7 @@
                         .add(VirtualNetworkAdminService.class, mockVnetAdminService)
                         .add(CodecService.class, codecService);
 
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
     }
 
     /**
diff --git a/web/api/src/test/java/org/onosproject/rest/resources/TopologyResourceTest.java b/web/api/src/test/java/org/onosproject/rest/resources/TopologyResourceTest.java
index 95a7416..4499a25 100644
--- a/web/api/src/test/java/org/onosproject/rest/resources/TopologyResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/resources/TopologyResourceTest.java
@@ -23,16 +23,15 @@
 import org.junit.Test;
 import org.onlab.osgi.ServiceDirectory;
 import org.onlab.osgi.TestServiceDirectory;
-import org.onlab.rest.BaseResource;
 import org.onosproject.codec.CodecService;
 import org.onosproject.codec.impl.CodecManager;
 import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultPort;
+import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Link;
-import org.onosproject.net.Device;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.DefaultPort;
 import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.device.DeviceServiceAdapter;
 import org.onosproject.net.provider.ProviderId;
@@ -43,8 +42,10 @@
 import org.onosproject.net.topology.TopologyCluster;
 import org.onosproject.net.topology.TopologyService;
 import org.onosproject.net.topology.TopologyServiceAdapter;
+
 import javax.ws.rs.client.WebTarget;
 import java.util.Set;
+
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.hasSize;
 import static org.hamcrest.Matchers.is;
@@ -183,7 +184,7 @@
                         .add(DeviceService.class, mockDeviceService)
                         .add(TopologyService.class, topologyService)
                         .add(CodecService.class, codecService);
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
     }
 
     /**
diff --git a/web/api/src/test/java/org/onosproject/rest/resources/VirtualNetworkWebResourceTest.java b/web/api/src/test/java/org/onosproject/rest/resources/VirtualNetworkWebResourceTest.java
index 97c703b..2ab185c 100644
--- a/web/api/src/test/java/org/onosproject/rest/resources/VirtualNetworkWebResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/resources/VirtualNetworkWebResourceTest.java
@@ -33,7 +33,6 @@
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
-import org.onlab.rest.BaseResource;
 import org.onosproject.codec.CodecService;
 import org.onosproject.codec.impl.CodecManager;
 import org.onosproject.incubator.net.virtual.DefaultVirtualDevice;
@@ -75,8 +74,16 @@
 import java.util.function.BiPredicate;
 import java.util.function.Function;
 
-import static org.easymock.EasyMock.*;
-import static org.hamcrest.Matchers.*;
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.fail;
@@ -187,7 +194,7 @@
                         .add(VirtualNetworkService.class, mockVnetService)
                         .add(CodecService.class, codecService);
 
-        BaseResource.setServiceDirectory(testDirectory);
+        setServiceDirectory(testDirectory);
     }
 
     /**
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/ApplicationResource.java b/web/gui/src/main/java/org/onosproject/ui/impl/ApplicationResource.java
index 16f288a..d8f5a44 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/ApplicationResource.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/ApplicationResource.java
@@ -41,7 +41,8 @@
 @Path("applications")
 public class ApplicationResource extends BaseResource {
 
-    static String lastInstalledAppName = null;
+    private static String lastInstalledAppName = null;
+    private static final Object LAST_INSTALLED_APP_NAME_LOCK = new Object();
 
 
     @Path("upload")
@@ -51,7 +52,9 @@
                            @FormDataParam("file") InputStream stream) throws IOException {
         ApplicationAdminService service = get(ApplicationAdminService.class);
         Application app = service.install(stream);
-        lastInstalledAppName = app.id().name();
+        synchronized (LAST_INSTALLED_APP_NAME_LOCK) {
+            lastInstalledAppName = app.id().name();
+        }
         if (Objects.equals(activate, "true")) {
             service.activate(app.id());
         }
@@ -87,4 +90,10 @@
         Application app = service.getApplication(appId);
         return Response.ok(app.icon()).build();
     }
+
+    static String getLastInstalledAppName() {
+        synchronized (LAST_INSTALLED_APP_NAME_LOCK) {
+            return lastInstalledAppName;
+        }
+    }
 }
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/ApplicationViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/ApplicationViewMessageHandler.java
index 984dce2..6ed92a5 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/ApplicationViewMessageHandler.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/ApplicationViewMessageHandler.java
@@ -172,7 +172,7 @@
             // If the ID was not specified in the payload, use the name of the
             // most recently uploaded app.
             if (isNullOrEmpty(id)) {
-                id = ApplicationResource.lastInstalledAppName;
+                id = ApplicationResource.getLastInstalledAppName();
             }
 
             ApplicationId appId = as.getId(id);
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java b/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java
index 07a0b0d..ea7e5ed 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java
@@ -218,15 +218,19 @@
     @Override
     public synchronized void onClose(int closeCode, String message) {
         try {
-            tokenService().revokeToken(sessionToken);
-            log.info("Session token revoked");
-        } catch (ServiceNotFoundException e) {
-            log.error("Unable to reference UiTokenService");
-        }
-        sessionToken = null;
+            try {
+                tokenService().revokeToken(sessionToken);
+                log.info("Session token revoked");
+            } catch (ServiceNotFoundException e) {
+                log.error("Unable to reference UiTokenService");
+            }
+            sessionToken = null;
 
-        topoSession.destroy();
-        destroyHandlersAndOverlays();
+            topoSession.destroy();
+            destroyHandlersAndOverlays();
+        } catch (Exception e) {
+            log.warn("Unexpected error", e);
+        }
         log.info("GUI client disconnected [close-code={}, message={}]",
                  closeCode, message);
     }
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocketServlet.java b/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocketServlet.java
index 24e02e6..cfd2025 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocketServlet.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocketServlet.java
@@ -38,6 +38,7 @@
     private static final long PING_DELAY_MS = 5000;
 
     private static UiWebSocketServlet instance;
+    private static final Object INSTANCE_LOCK = new Object();
 
     private ServiceDirectory directory = new DefaultServiceDirectory();
 
@@ -50,19 +51,23 @@
      * Closes all currently open UI web-sockets.
      */
     public static void closeAll() {
-        if (instance != null) {
-            instance.isStopped = true;
-            instance.sockets.forEach(UiWebSocket::close);
-            instance.sockets.clear();
-            instance.pruner.cancel();
-            instance.timer.cancel();
+        synchronized (INSTANCE_LOCK) {
+            if (instance != null) {
+                instance.isStopped = true;
+                instance.sockets.forEach(UiWebSocket::close);
+                instance.sockets.clear();
+                instance.pruner.cancel();
+                instance.timer.cancel();
+            }
         }
     }
 
     @Override
     public void init() throws ServletException {
         super.init();
-        instance = this;
+        synchronized (INSTANCE_LOCK) {
+            instance = this;
+        }
         timer.schedule(pruner, PING_DELAY_MS, PING_DELAY_MS);
     }
 
@@ -93,8 +98,10 @@
      * @param payload message payload
      */
     static void sendToAll(String type, ObjectNode payload) {
-        if (instance != null) {
-            instance.sockets.forEach(ws -> ws.sendMessage(type, payload));
+        synchronized (INSTANCE_LOCK) {
+            if (instance != null) {
+                instance.sockets.forEach(ws -> ws.sendMessage(type, payload));
+            }
         }
     }
 
@@ -106,9 +113,11 @@
      * @param payload  message payload
      */
     static void sendToUser(String userName, String type, ObjectNode payload) {
-        if (instance != null) {
-            instance.sockets.stream().filter(ws -> userName.equals(ws.userName()))
-                    .forEach(ws -> ws.sendMessage(type, payload));
+        synchronized (INSTANCE_LOCK) {
+            if (instance != null) {
+                instance.sockets.stream().filter(ws -> userName.equals(ws.userName()))
+                        .forEach(ws -> ws.sendMessage(type, payload));
+            }
         }
     }
 
diff --git a/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Action_el.properties b/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Action_el.properties
new file mode 100644
index 0000000..88b41c3
--- /dev/null
+++ b/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Action_el.properties
@@ -0,0 +1,25 @@
+#

+# Copyright 2017-present Open Networking Laboratory

+#

+# Licensed under the Apache License, Version 2.0 (the "License");

+# you may not use this file except in compliance with the License.

+# You may obtain a copy of the License at

+#

+#     http://www.apache.org/licenses/LICENSE-2.0

+#

+# Unless required by applicable law or agreed to in writing, software

+# distributed under the License is distributed on an "AS IS" BASIS,

+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+# See the License for the specific language governing permissions and

+# limitations under the License.

+#

+#

+

+install=Εγκατάσταση

+start=Εκκίνηση

+pause=Παύση

+resume=Επανέναρξη

+stop=Τερματισμός

+uninstall=Απεγκατάσταση

+activate=Ενεργοποίηση

+deactivate=Απενεργοποίηση
\ No newline at end of file
diff --git a/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Network_el.properties b/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Network_el.properties
new file mode 100644
index 0000000..b16f5ca
--- /dev/null
+++ b/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Network_el.properties
@@ -0,0 +1,49 @@
+#

+# Copyright 2017-present Open Networking Laboratory

+#

+# Licensed under the Apache License, Version 2.0 (the "License");

+# you may not use this file except in compliance with the License.

+# You may obtain a copy of the License at

+#

+#     http://www.apache.org/licenses/LICENSE-2.0

+#

+# Unless required by applicable law or agreed to in writing, software

+# distributed under the License is distributed on an "AS IS" BASIS,

+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+# See the License for the specific language governing permissions and

+# limitations under the License.

+#

+#

+# --- Elements (Singular)

+node=Κόμβος

+topology=Τοπολογία

+network=Δίκτυο

+region=Περιοχή

+device=Συσκευή

+host=Τερματικό

+link=Σύνδεσμος

+

+# --- Elements (Plural)

+nodes=Κόμβοι

+topologies=Τοπολογίες

+networks=Δίκτυα

+regions=Περιοχές

+devices=Συσκευές

+hosts=Τερματικά

+links=Σύνδεσμοι

+

+# --- Element IDs

+node_id=ID κόμβου

+region_id=ID περιοχής

+device_id=ID συσκευής

+host_id=ID τερματικού

+link_id=ID συνδέσμου

+

+# --- Protocol terms

+protocol=πρωτόκολλο

+ip=IP

+ip_address=IP διεύθυνση

+tcp_port=TCP θύρα

+mac=MAC

+mac_address=MAC διεύθυνση

+uri=URI
\ No newline at end of file
diff --git a/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Props_el.properties b/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Props_el.properties
new file mode 100644
index 0000000..18c56b0
--- /dev/null
+++ b/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Props_el.properties
@@ -0,0 +1,34 @@
+#

+# Copyright 2017-present Open Networking Laboratory

+#

+# Licensed under the Apache License, Version 2.0 (the "License");

+# you may not use this file except in compliance with the License.

+# You may obtain a copy of the License at

+#

+#     http://www.apache.org/licenses/LICENSE-2.0

+#

+# Unless required by applicable law or agreed to in writing, software

+# distributed under the License is distributed on an "AS IS" BASIS,

+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+# See the License for the specific language governing permissions and

+# limitations under the License.

+#

+#

+

+# Typically used as table column headers or property labels

+

+chassis_id=ID πλαισίου

+hw_version=Έκδοση υλικού

+sw_version=Έκδοση λογισμικού

+serial_number=Σειριακός αριθμός

+app_id=ID εφαρμογής

+

+type=Τύπος

+vendor=Προμηθευτής

+icon=Εικόνα

+title=Τίτλος

+state=Κατάσταση

+category=Κατηγορία

+version=Έκδοση

+origin=Προέλευση

+role=Ρόλος
\ No newline at end of file
diff --git a/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/State_el.properties b/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/State_el.properties
new file mode 100644
index 0000000..741d34c
--- /dev/null
+++ b/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/State_el.properties
@@ -0,0 +1,25 @@
+

+#

+# Copyright 2017-present Open Networking Laboratory

+#

+# Licensed under the Apache License, Version 2.0 (the "License");

+# you may not use this file except in compliance with the License.

+# You may obtain a copy of the License at

+#

+#     http://www.apache.org/licenses/LICENSE-2.0

+#

+# Unless required by applicable law or agreed to in writing, software

+# distributed under the License is distributed on an "AS IS" BASIS,

+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+# See the License for the specific language governing permissions and

+# limitations under the License.

+#

+#

+

+total=Σύνολο

+active=Ενεργά

+started=Ξεκίνησαν

+stopped=Σταμάτησαν

+

+last_updated=Τελευταία ενημέρωση

+last_modified=Τελευταία τροποποίηση
\ No newline at end of file
diff --git a/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Ui_el.properties b/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Ui_el.properties
new file mode 100644
index 0000000..7543ba7
--- /dev/null
+++ b/web/gui/src/main/resources/org/onosproject/ui/lion/core/common/Ui_el.properties
@@ -0,0 +1,27 @@
+#

+# Copyright 2017-present Open Networking Laboratory

+#

+# Licensed under the Apache License, Version 2.0 (the "License");

+# you may not use this file except in compliance with the License.

+# You may obtain a copy of the License at

+#

+#     http://www.apache.org/licenses/LICENSE-2.0

+#

+# Unless required by applicable law or agreed to in writing, software

+# distributed under the License is distributed on an "AS IS" BASIS,

+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+# See the License for the specific language governing permissions and

+# limitations under the License.

+#

+#

+

+# Common button text

+ok=Οκ

+cancel=Άκυρο

+

+# Gesture text

+click=Κάντε κλικ

+scroll_down=Μετακίνηση προς τα κάτω

+

+# Common control button tooltips

+tt_ctl_auto_refresh=Εναλλαγή αυτόματης ανανέωσης
\ No newline at end of file
diff --git a/web/gui/src/main/resources/org/onosproject/ui/lion/core/enums/DeviceEnums_el.properties b/web/gui/src/main/resources/org/onosproject/ui/lion/core/enums/DeviceEnums_el.properties
new file mode 100644
index 0000000..9c5b0cd
--- /dev/null
+++ b/web/gui/src/main/resources/org/onosproject/ui/lion/core/enums/DeviceEnums_el.properties
@@ -0,0 +1,34 @@
+#

+# 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.

+#

+

+# display names for Device.Type constants

+switch=μεταγωγέας

+router=δρομολογητής

+roadm=roadm

+otn=otn

+roadm_otn=roadm otn

+firewall=τείχος προστασίας

+balancer=εξισορροπιστής

+ips=ips

+ids=ids

+controller=ελεγχτής

+virtual=εικονικός

+fiber_switch=μεταγωγέας ίνας

+microwave=μικροκυματικός

+olt=olt

+onu=onu

+optical_amplifier=οπτικός ενισχυτής

+other=άλλο
\ No newline at end of file
diff --git a/web/gui/src/main/resources/org/onosproject/ui/lion/core/enums/LinkEnums_el.properties b/web/gui/src/main/resources/org/onosproject/ui/lion/core/enums/LinkEnums_el.properties
new file mode 100644
index 0000000..5817212
--- /dev/null
+++ b/web/gui/src/main/resources/org/onosproject/ui/lion/core/enums/LinkEnums_el.properties
@@ -0,0 +1,27 @@
+#

+# 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.

+#

+

+# display names for Link.Type constants

+direct=άμεση

+indirect=έμμεση

+edge=ακμή

+tunnel=σήραγγα

+optical=οπτικό

+virtual=εικονικό

+

+# display names for Link.State constants

+active=ενεργό

+inactive=ανενεργό
\ No newline at end of file
diff --git a/web/gui/src/main/resources/org/onosproject/ui/lion/core/fw/Mast_el.properties b/web/gui/src/main/resources/org/onosproject/ui/lion/core/fw/Mast_el.properties
new file mode 100644
index 0000000..2c22c2b
--- /dev/null
+++ b/web/gui/src/main/resources/org/onosproject/ui/lion/core/fw/Mast_el.properties
@@ -0,0 +1,32 @@
+#

+# Copyright 2017-present Open Networking Laboratory

+#

+# Licensed under the Apache License, Version 2.0 (the "License");

+# you may not use this file except in compliance with the License.

+# You may obtain a copy of the License at

+#

+#     http://www.apache.org/licenses/LICENSE-2.0

+#

+# Unless required by applicable law or agreed to in writing, software

+# distributed under the License is distributed on an "AS IS" BASIS,

+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+# See the License for the specific language governing permissions and

+# limitations under the License.

+#

+#

+# Localization strings for Masthead

+

+# Tooltip for context-sensitive help button [?]

+tt_help=Εμφάνιση βοήθειας

+

+# unknown user

+unknown_user=Άγνωστος χρήστης

+

+# Logout button

+logout=Έξοδος

+

+# UI components added/removed, etc.

+uicomp_added=Προστέθηκαν νέα στοιχεία GUI.

+uicomp_removed=Ορισμένα στοιχεία GUI καταργήθηκαν.

+ui_ok_to_update=Πατήστε OK για ενημέρωση του GUI.

+confirm_refresh_title=Επιβεβαίωση ανανέωσης GUI.
\ No newline at end of file
diff --git a/web/gui/src/main/resources/org/onosproject/ui/lion/core/fw/Nav_el.properties b/web/gui/src/main/resources/org/onosproject/ui/lion/core/fw/Nav_el.properties
new file mode 100644
index 0000000..29eede1
--- /dev/null
+++ b/web/gui/src/main/resources/org/onosproject/ui/lion/core/fw/Nav_el.properties
@@ -0,0 +1,29 @@
+#

+# Copyright 2017-present Open Networking Laboratory

+#

+# Licensed under the Apache License, Version 2.0 (the "License");

+# you may not use this file except in compliance with the License.

+# You may obtain a copy of the License at

+#

+#     http://www.apache.org/licenses/LICENSE-2.0

+#

+# Unless required by applicable law or agreed to in writing, software

+# distributed under the License is distributed on an "AS IS" BASIS,

+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+# See the License for the specific language governing permissions and

+# limitations under the License.

+#

+#

+# Localization strings for Navigation Panel

+

+# Category headers

+cat_platform=Πλατφόρμα

+cat_network=Δίκτυο

+cat_other=Άλλο

+

+# Note that contributed views will be adding their own localized titles

+#  for their nav item, with a property key "nav_item_{viewID}".

+#

+# E.g. in the Device view properties bundle there will be:

+#

+#  nav_item_device=Devices
\ No newline at end of file
diff --git a/web/gui/src/main/resources/org/onosproject/ui/lion/core/fw/QuickHelp_el.properties b/web/gui/src/main/resources/org/onosproject/ui/lion/core/fw/QuickHelp_el.properties
new file mode 100644
index 0000000..e053af5
--- /dev/null
+++ b/web/gui/src/main/resources/org/onosproject/ui/lion/core/fw/QuickHelp_el.properties
@@ -0,0 +1,32 @@
+#

+# Copyright 2017-present Open Networking Laboratory

+#

+# Licensed under the Apache License, Version 2.0 (the "License");

+# you may not use this file except in compliance with the License.

+# You may obtain a copy of the License at

+#

+#     http://www.apache.org/licenses/LICENSE-2.0

+#

+# Unless required by applicable law or agreed to in writing, software

+# distributed under the License is distributed on an "AS IS" BASIS,

+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+# See the License for the specific language governing permissions and

+# limitations under the License.

+#

+#

+# Common text in the Quick Help panel

+

+# Panel title

+qh_title=Γρήγορη βοήθεια

+

+# Hint for \ and / keys

+qh_hint_show_hide_qh=Εμφάνιση / Απόκρυψη Γρήγορης βοήθειας

+

+# Escape key hint

+qh_hint_esc=Διαγραφή διαλόγου ή ακύρωση επιλογών

+

+# T key hint : toggle the ui theme (between light / dark)

+qh_hint_t=Εναλλαγή θέματος

+

+# Common for a lot of views adding their own escape-key handler

+qh_hint_close_detail=Κλείσιμο πίνακα λεπτομερειών
\ No newline at end of file
diff --git a/web/gui/src/main/resources/org/onosproject/ui/lion/core/view/App_el.properties b/web/gui/src/main/resources/org/onosproject/ui/lion/core/view/App_el.properties
new file mode 100644
index 0000000..67547b0
--- /dev/null
+++ b/web/gui/src/main/resources/org/onosproject/ui/lion/core/view/App_el.properties
@@ -0,0 +1,45 @@
+#

+# Copyright 2017-present Open Networking Laboratory

+#

+# Licensed under the Apache License, Version 2.0 (the "License");

+# you may not use this file except in compliance with the License.

+# You may obtain a copy of the License at

+#

+#     http://www.apache.org/licenses/LICENSE-2.0

+#

+# Unless required by applicable law or agreed to in writing, software

+# distributed under the License is distributed on an "AS IS" BASIS,

+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+# See the License for the specific language governing permissions and

+# limitations under the License.

+#

+#

+

+# Text that appears in the navigation panel

+nav_item_app=Εφαρμογές

+

+# View title

+title_apps=Εφαρμογές

+

+# Control button tooltips

+tt_ctl_upload=Ανέβασμα εφαρμογής (.oar αρχείο)

+tt_ctl_activate=Ενεργοποίηση επιλεγμένης εφαρμογής

+tt_ctl_deactivate=Απενεργοποίηση επιλεγμένης εφαρμογής

+tt_ctl_uninstall=Απεγκατάσταση επιλεγμένης εφαρμογής

+

+# Quick-Help panel

+qh_hint_esc=Αποεπιλογή εφαρμογής

+qh_hint_click_row=Επιλογή / Αποεπιλογή εφαρμογής

+qh_hint_scroll_down=Περισσότερες εφαρμογές

+

+# App details panel

+dp_features=Χαρακτηριστικά

+dp_required_apps=Απαραίτητες εφαρμογές

+dp_permissions=Άδειες

+

+# App dialog panel

+dlg_confirm_action=Επιβεβαίωση Ενέργειας

+

+dlg_warn_deactivate=Απενεργοποίηση ή απεγκατάσταση αυτού του στοιχείου \

+   Μπορεί να έχει σοβαρές αρνητικές συνέπειες!

+dlg_warn_own_risk=** Ενεργείστε με δική σας ευθύνη **
\ No newline at end of file
diff --git a/web/gui/src/main/resources/org/onosproject/ui/lion/core/view/Cluster_el.properties b/web/gui/src/main/resources/org/onosproject/ui/lion/core/view/Cluster_el.properties
new file mode 100644
index 0000000..e10bbe6
--- /dev/null
+++ b/web/gui/src/main/resources/org/onosproject/ui/lion/core/view/Cluster_el.properties
@@ -0,0 +1,27 @@
+#

+# Copyright 2017-present Open Networking Laboratory

+#

+# Licensed under the Apache License, Version 2.0 (the "License");

+# you may not use this file except in compliance with the License.

+# You may obtain a copy of the License at

+#

+#     http://www.apache.org/licenses/LICENSE-2.0

+#

+# Unless required by applicable law or agreed to in writing, software

+# distributed under the License is distributed on an "AS IS" BASIS,

+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+# See the License for the specific language governing permissions and

+# limitations under the License.

+#

+#

+# Cluster view specific text

+

+# Text that appears in the navigation panel

+nav_item_cluster=Κόμβοι συστάδας

+

+# View title

+title_cluster_nodes=Κόμβοι συστάδας

+

+# Quick-Help panel

+qh_hint_click=Επιλέξτε μια γραμμή για να εμφανίσετε λεπτομέρειες κόμβου συστάδας

+qh_hint_scroll_down=Δείτε διαθέσιμους κόμβους στη συστάδα
\ No newline at end of file
diff --git a/web/gui/src/main/resources/org/onosproject/ui/lion/core/view/Topo_el.properties b/web/gui/src/main/resources/org/onosproject/ui/lion/core/view/Topo_el.properties
new file mode 100644
index 0000000..356592d9
--- /dev/null
+++ b/web/gui/src/main/resources/org/onosproject/ui/lion/core/view/Topo_el.properties
@@ -0,0 +1,143 @@
+#

+# 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.

+#

+#

+

+# Text that appears in the navigation panel

+nav_item_topo=Τοπολογία

+

+# Message when no devices are connected

+no_devices_are_connected=Δεν υπάρχουν συνδεδεμένες συσκευές

+

+# Action Key Toolbar Tooltips ...

+tbtt_tog_instances=Εναλλαγή πίνακα στιγμιοτύπων του ONOS

+tbtt_tog_summary=Εναλλαγή περιληπτικού πίνακα ONOS

+tbtt_tog_use_detail=Απενεργοποίηση / Ενεργοποίηση πίνακα λεπτομερειών

+tbtt_tog_host=Εναλλαγή ορατότητας υπολογιστών

+tbtt_tog_offline=Εναλλαγή ορατότητας εκτός σύνδεσης

+tbtt_tog_porthi=Εναλλαγή επισήμανσης θυρών

+tbtt_bad_links=Εμφάνιση κακών συνδέσμων

+tbtt_tog_map=Εναλλαγή γεωγραφικού χάρτη στο φόντο

+tbtt_sel_map=Επιλογή γεωγραφικού χάρτη στο φόντο

+tbtt_tog_sprite=Εναλλαγή επιπέδου sprite

+tbtt_reset_loc=Επαναφορά τοποθεσιών κόμβου

+tbtt_tog_oblique=Εναλλαγή πλάγιας προβολής (πειραματικά)

+tbtt_cyc_layers=Στρώμα κόμβων κύκλων

+tbtt_cyc_dev_labs=Κύκλος ετικετών συσκευής

+tbtt_cyc_host_labs=Κύκλος ετικετών υποδοχής

+tbtt_unpin_node=Αποσύνδεση κόμβου (μετακινήστε το ποντίκι πάνω)

+tbtt_reset_zoom=Επαναφορά πανοραμτικής όψης/ζουμ

+tbtt_tog_toolbar=Εναλλαγή γραμμής εργαλείων

+tbtt_eq_master=Εξίσωση ρόλων κυριότητας

+

+# Quick Help Gestures

+qh_gest_click=Επέλεξε το αντικείμενο και δείξε  πληροφορίες

+qh_gest_shift_click=Εναλλαγή κατάστασης επιλογής

+qh_gest_drag=Επανατοποθέτηση και σήμανση της συσκευής/υπολογιστή

+qh_gest_cmd_scroll=Μεγένθυση / Σμίκρυνση

+qh_gest_cmd_drag=Πανοραμική όψη

+

+# Flash Messages

+fl_background_map=Χάρτης φόντου

+fl_sprite_layer=sprite επίπεδο

+fl_pan_zoom_reset=Επαναφορά πανοραμικής όψης και ζουμ

+fl_eq_masters=Εξισορρόπηση ρόλων κυριότητας

+

+fl_device_labels_hide=Απόκρυψη ετικετών συσκευών

+fl_device_labels_show_friendly=Εμφάνιση φιλικών ετικετών συσκευών

+fl_device_labels_show_id=Εμφάνιση ετικετών ταυτότητας συσκευής

+fl_host_labels_show_friendly=Εμφάνιση φιλικών ετικετών υπολογιστών

+fl_host_labels_show_ip=Εμφάνιση διευθύνσεων IP υπολογιστών

+fl_host_labels_show_mac=Εμφάνιση διευθύνσεων MAC υπολογιστών

+

+fl_offline_devices=Συσκευές εκτός σύνδεσης

+fl_bad_links=Κακοί σύνδεσμοι

+fl_reset_node_locations=Επαναφορά τοποθεσιών κόμβων

+

+fl_layer_all=Εμφάνιση όλων των επιπέδων

+fl_layer_pkt=Εμφάνιση επιπέδου πακέτων

+fl_layer_opt=Εμφάνιση οπτικού επιπέδου

+

+fl_panel_instances=Πίνακας στιγμιοτύπων

+fl_panel_summary=Πίνακας περίληψης

+fl_panel_details=Πίνακας λεπτομεριών

+

+fl_port_highlighting=Σήμανση θυρών

+

+fl_oblique_view=Πλάγια όψη

+fl_normal_view=Κανονική όψη

+

+fl_monitoring_canceled=Ακύρωση παρακολούθησης

+fl_selecting_intent=Επιλογή κατάστασης

+

+# Core Overlays

+ov_tt_protected_intents=Προστατευόμενη επικάλυψη καταστάσεων

+ov_tt_traffic=Επικάλυψη κίνησης

+ov_tt_none=Καμία επικάλυψη

+

+# Traffic Overlay

+tr_btn_create_h2h_flow=Δημιουργία ροής από υπολογιστή σε υπολογιστή

+tr_btn_create_msrc_flow=Δημιουργία ροής πολλαπλών πηγών

+tr_btn_show_device_flows=Εμφάνιση ροών συσκευών

+tr_btn_show_related_traffic=Εμφάνιση σχετικής κίνησης

+tr_btn_cancel_monitoring=Ακύρωση παρακολούθησης κίνησης

+tr_btn_monitor_all=Παρακολούθηση όλης της κίνησης

+tr_btn_show_dev_link_flows=Εμφάνιση των ροών συνδέσμων των συσκευών

+tr_btn_show_all_rel_intents=Εμφάνιση όλων των σχετικών καταστάσεων

+tr_btn_show_prev_rel_intent=Προηγούμενη σχετική κατάσταση

+tr_btn_show_next_rel_intent=Επόμενη σχετική κατάσταση

+tr_btn_monitor_sel_intent=Παρακολούθηση κίνησης ή επιλεγμένης κατάστασης

+tr_fl_fstats_bytes=Στατιστικά ροών (ψηφιολέξεις)

+tr_fl_pstats_bits=Στατιστικά θυρών (δυαδικά ψηφία / δευτερόλεπτο)

+tr_fl_pstats_pkts=Στατιστικά θυρών (πακέτα / δευτερόλεπτο)

+tr_fl_dev_flows=Ροές συσκευών

+tr_fl_rel_paths=Σχετικά μονοπάτια

+tr_fl_prev_rel_int=Προηγούμενη σχετική κατάσταση

+tr_fl_next_rel_int=Επόμενη σχετική κατάσταση

+tr_fl_traf_on_path=Κίνηση σε επιλεγμένο μονοπάτι

+tr_fl_h2h_flow_added=Προσθήκη ροής από υπολογιστή σε υπολογιστή

+tr_fl_multisrc_flow=Ροή πολλών πηγών

+

+# Button tooltips

+btn_show_view_device=Εμφανιση προβολής συσκευών

+btn_show_view_flow=Εμφάνιση προβολής ροών για τη συσκευή

+btn_show_view_port=Εμφάνιση προβολής θυρών για τη συσκευή

+btn_show_view_group=Εμφάνιση προβολής ομάδων για τη συσκευή

+btn_show_view_meter=Εμφάνιση προβολής μετρητών για τη συσκευή

+

+# Panel Titles

+title_select_map=Επιλογή χάρτη

+title_panel_summary=Περίληψη ONOS

+title_selected_items=Επιλεγμένα αντικείμενα

+title_edge_link=Συνδεσμός ακμής

+title_infra_link=Σύνδεσμος υποδομής

+

+# Custom Panel Labels / Values

+lp_label_friendly=Φιλική

+

+lp_label_a_type=A τύπος

+lp_label_a_id=A ταυτότητα

+lp_label_a_friendly=A όνομα

+lp_label_a_port=A θύρα

+

+lp_label_b_type=B τύπος

+lp_label_b_id=B ταυτότητα

+lp_label_b_friendly=B όνομα

+lp_label_b_port=B θύρα

+

+lp_label_a2b=A σε B

+lp_label_b2a=B σε A

+

+lp_value_no_link=[Κανένας σύνδεσμος]