Initial import of Microsemi Driver

Change-Id: I431d5f2c18e0b66a84c36273c3d9f0b84f223841

Added in BUCK files for building driver

Change-Id: I70681327f5b89f67e904c45d5974ab393652d51f

Corrected some syntax errors

Change-Id: I11150cc499c212005f80619e3900e747f1c23d96

Updated pom file to clean build

Change-Id: I6613ddc9e6802aa882e716cf04df210249870835

Added in utility functions for EA1000 Init

Change-Id: I51ffe0cf0daf9ffcea0e2479ee9982fcd1755440

Added YMS code to Microsemi Driver

Change-Id: I6f2a14e454c6909bf9e9f6025321c74c98c13c72

Updated driver to work with YMS and YCH

Change-Id: If7dbe3cd5bd1b6f902d09d6b2dc3895605d70f70

Implemented IetfSystemManager as a service and call on YMS as a service

Change-Id: If1c5e8482b1f53f578a3b0b770accd50024111cf

Moved YMS calls over in to Yang Service implementation

Change-Id: I044aad06f1ef7452bc48e88987787a683666cd72

improved unit test for IetfSystemManager

Change-Id: I48fbf831e7e5ca0e1ef3de8288e56da1b5ebb7a4

Major changes to IetfSystemManager to work in live system

Change-Id: I6e3aa118ba422151f314b9a666860d90905c9929

Added in retry mechanism for DeviceDescription to wait for YCH

Change-Id: If8e0f2c2f315ffd6db15627a11382a00217dd262

Added in implementation of MseaSaFiltering and unit tests

Change-Id: I34bf888e0e732bd4664d1fb8ef5abb679b1506fe

Updated driver with unit tests for MseaSaFiltering

Change-Id: I7ea2407a546622ff55d1ab21610c45697546d632

Modified removeFlowRules of Ea1000FlowRuleProgrammable

Change-Id: Ibb4a555f61887a8e6e42af588bb42f7b70f58efb

Added in manager for MseaUniEvc service with unit tests

Change-Id: Idc5853f46051548973f52a0659f7f88982ff960c

Implemented getFlowEntries() for EVCs from EA1000

Change-Id: Ie85dadfa7760f0b30a9bdf6ccd09cca9f097fff9

Added in translation of FlowRules in to EVC on EA1000

Change-Id: Icfb65171c3300c96b3ca4e18cbd327f0ed2190be

Added in handling of FlowRule deletion including complex ceVlanMaps

Change-Id: I7fd0bb0ef04d1b40e4b7d6a6db7f7ee662329780

Updated Service entries for new onos-yang-tools

Change-Id: I44e655202f3a45073e1e16f83737caed6e01afa8

Revert "Updated Service entries for new onos-yang-tools"

This reverts commit 642b550ef1de12ed59bad2eaa3a2da414d2e5e59.

Improved timeout mechanism for YANG model loading

Change-Id: If744ecd206372e822edf2b736c83226321a12256

Minor edits of EVC creation

Change-Id: Ib0a4763deaf6dce37625ba77f5095b39cd98272d

Added in CustomEvc and supporting classes

Change-Id: Iad60eb1bcd48d2aec55b894b2d419b51852c3b2f

Created CeVlanUtils to resolve loading problem

Change-Id: I0d63931ad2c5ad2725861ebc7dccc4d5fe7b9298

Modified startup check

Change-Id: I6e6bcfa7e615044cb08fe7ee2f8a6c8b89aabb21

Modified handlin of flow rules

Change-Id: I965a79c23298866122aeb94c6d9d584aafee3bd5

Fixed problem with ceVlanMap

Change-Id: If1458c35d0b95b5b25b6636f098292f9e91c06c6

Minor Pom edits

Change-Id: I5cefb18674aa04b1f50bd7e2306260c1c3ad3814

Commented out extension references in YANG files to avoid onos-yang-tools problems

Change-Id: I32fdb34c4f476f495fe28e75d0f410aaf14e2ec1

Corrected error in removing 0 in CeVlanMapUtils

Change-Id: I8cd1fd02788b81c2613364d5639ef6e090057f80

Changes in YMS to accomodate EA1000 driver

Change-Id: I6ae2b9bd2be49eae8d4ad2f929dfe3214c514550
diff --git a/drivers/microsemi/ea1000driver/src/main/java/org/onosproject/drivers/microsemi/EA1000FlowRuleProgrammable.java b/drivers/microsemi/ea1000driver/src/main/java/org/onosproject/drivers/microsemi/EA1000FlowRuleProgrammable.java
new file mode 100644
index 0000000..3c593d6
--- /dev/null
+++ b/drivers/microsemi/ea1000driver/src/main/java/org/onosproject/drivers/microsemi/EA1000FlowRuleProgrammable.java
@@ -0,0 +1,942 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.drivers.microsemi;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.math.BigInteger;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Semaphore;
+import java.util.stream.Collectors;
+
+import org.onlab.packet.EthType;
+import org.onlab.packet.EthType.EtherType;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.VlanId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.drivers.microsemi.yang.MseaSaFilteringNetconfService;
+import org.onosproject.drivers.microsemi.yang.MseaUniEvcServiceNetconfService;
+import org.onosproject.drivers.microsemi.yang.UniSide;
+import org.onosproject.drivers.microsemi.yang.utils.CeVlanMapUtils;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.flow.DefaultFlowEntry;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowEntry.FlowEntryState;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleProgrammable;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criteria;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.Criterion.Type;
+import org.onosproject.net.flow.criteria.PortCriterion;
+import org.onosproject.net.flow.criteria.VlanIdCriterion;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanHeaderInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
+import org.onosproject.net.meter.MeterId;
+import org.onosproject.netconf.NetconfController;
+import org.onosproject.netconf.NetconfException;
+import org.onosproject.netconf.NetconfSession;
+import org.onosproject.netconf.TargetConfig;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.sa.filtering.rev20160412.MseaSaFiltering;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.sa.filtering.rev20160412.MseaSaFilteringOpParam;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.sa.filtering.rev20160412.mseasafiltering.DefaultSourceIpaddressFiltering;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.sa.filtering.rev20160412.mseasafiltering.SourceIpaddressFiltering;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.sa.filtering.rev20160412.mseasafiltering.sourceipaddressfiltering.DefaultInterfaceEth0;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.sa.filtering.rev20160412.mseasafiltering.sourceipaddressfiltering.InterfaceEth0;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.sa.filtering.rev20160412.mseasafiltering.sourceipaddressfiltering.interfaceeth0.DefaultSourceAddressRange;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.sa.filtering.rev20160412.mseasafiltering.sourceipaddressfiltering.interfaceeth0.FilterAdminStateEnum;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.sa.filtering.rev20160412.mseasafiltering.sourceipaddressfiltering.interfaceeth0.SourceAddressRange;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.types.rev20160229.mseatypes.Identifier45;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.types.rev20160229.mseatypes.ServiceListType;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.types.rev20160229.mseatypes.VlanIdType;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.MseaUniEvcService;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.MseaUniEvcServiceOpParam;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.DefaultMefServices;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.MefServices;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.evcperuniextensionattributes.DefaultFlowMapping;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.evcperuniextensionattributes.FlowMapping;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.evcperuniextensionattributes.TagManipulation;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.evcperuniextensionattributes.tagmanipulation.DefaultTagOverwrite;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.evcperuniextensionattributes.tagmanipulation.DefaultTagPop;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.evcperuniextensionattributes.tagmanipulation.DefaultTagPush;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.evcperuniextensionattributes.tagmanipulation.TagOverwrite;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.evcperuniextensionattributes.tagmanipulation.TagPop;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.evcperuniextensionattributes.tagmanipulation.TagPush;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.evcperuniextensionattributes.tagmanipulation.tagpush.tagpush.PushTagTypeEnum;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.DefaultProfiles;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.DefaultUni;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.Profiles;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.Uni;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.profiles.BwpGroup;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.profiles.DefaultBwpGroup;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.uni.CustomEvc;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.uni.DefaultEvc;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.uni.Evc;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.uni.UniSideInterfaceAssignmentEnum;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.uni.evc.DefaultEvcPerUni;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.uni.evc.EvcPerUni;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.uni.evc.evcperuni.CustomEvcPerUnic;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.uni.evc.evcperuni.CustomEvcPerUnin;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.uni.evc.evcperuni.DefaultEvcPerUnic.EvcPerUnicBuilder;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.uni.evc.evcperuni.DefaultEvcPerUnin.EvcPerUninBuilder;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.uni.evc.evcperuni.EvcPerUnic;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.uni.evc.evcperuni.EvcPerUnin;
+import org.slf4j.Logger;
+
+/**
+ * An implementation of the FlowRuleProgrammable behaviour for the EA10000 device.
+ *
+ * This device is not a native Open Flow device. It has only a NETCONF interface for configuration
+ * status retrieval and notifications. It supports only a small subset of OpenFlow rules.<br>
+ *
+ * The device supports only:<br>
+ * 1) Open flow rules that blocks certain IP address ranges, but only those incoming on Port 0
+ *    and has a limit of 10 such rules<br>
+ * 2) Open flow rules that PUSH, POP and OVERWRITE VLAN tags on both ports. This can push and overwrite
+ *    both C-TAGs (0x8100) and S-TAGs (0x88a8).
+ */
+public class EA1000FlowRuleProgrammable extends AbstractHandlerBehaviour implements FlowRuleProgrammable {
+
+    protected final Logger log = getLogger(getClass());
+    public static final String MICROSEMI_DRIVERS = "com.microsemi.drivers";
+    public static final int PRIORITY_DEFAULT = 50000;
+    //To protect the NETCONF session from concurrent access across flow addition and removal
+    static Semaphore sessionMutex = new Semaphore(1);
+
+    /**
+     * Get the flow entries that are present on the EA1000.
+     * Since the EA1000 does not have any 'real' flow entries these are retrieved from 2 configuration
+     * areas on the EA1000 NETCONF model - from SA filtering YANG model and from EVC UNI YANG model.<br>
+     * The flow entries must match exactly the FlowRule entries in the ONOS store. If they are not an
+     * exact match the device will be requested to remove those flows and the FlowRule will stay in a
+     * PENDING_ADD state.
+     * @return A collection of Flow Entries
+     */
+    @Override
+    public Collection<FlowEntry> getFlowEntries() {
+        Collection<FlowEntry> flowEntryCollection = new HashSet<FlowEntry>();
+
+        UniSideInterfaceAssignmentEnum portAssignment = UniSideInterfaceAssignmentEnum.UNI_C_ON_HOST;
+        NetconfController controller = checkNotNull(handler().get(NetconfController.class));
+        NetconfSession session = controller.getDevicesMap().get(handler().data().deviceId()).getSession();
+        CoreService coreService = checkNotNull(handler().get(CoreService.class));
+        ApplicationId appId = coreService.getAppId(MICROSEMI_DRIVERS);
+        MseaSaFilteringNetconfService mseaSaFilteringService =
+                (MseaSaFilteringNetconfService) checkNotNull(handler().get(MseaSaFilteringNetconfService.class));
+        MseaUniEvcServiceNetconfService mseaUniEvcServiceSvc =
+                (MseaUniEvcServiceNetconfService) checkNotNull(handler().get(MseaUniEvcServiceNetconfService.class));
+        log.debug("getFlowEntries() called on EA1000FlowRuleProgrammable");
+
+        //First get the MseaSaFiltering rules
+        SourceIpaddressFiltering.SourceIpaddressFilteringBuilder sipBuilder =
+                new DefaultSourceIpaddressFiltering.SourceIpaddressFilteringBuilder();
+
+        MseaSaFilteringOpParam.MseaSaFilteringBuilder opBuilder =
+                new MseaSaFilteringOpParam.MseaSaFilteringBuilder();
+        MseaSaFilteringOpParam mseaSaFilteringFilter =
+                (MseaSaFilteringOpParam) opBuilder
+                .sourceIpaddressFiltering(sipBuilder.build())
+                .build();
+        try {
+            MseaSaFiltering saFilteringCurrent =
+                    mseaSaFilteringService.getMseaSaFiltering(mseaSaFilteringFilter, session);
+            if (saFilteringCurrent != null) {
+                flowEntryCollection.addAll(
+                        convertSaFilteringToFlowRules(saFilteringCurrent, appId));
+            }
+        } catch (NetconfException e) {
+            log.warn("Unexpected error on getFlowEntries", e);
+        }
+
+
+        //Then get the EVCs - there will be a flow entry per EVC
+        Uni.UniBuilder uniBuilder = new DefaultUni.UniBuilder();
+
+        MefServices.MefServicesBuilder mefBuilder = new DefaultMefServices.MefServicesBuilder();
+        MefServices mefServices = mefBuilder.uni(uniBuilder.build()).build();
+
+        MseaUniEvcService.MseaUniEvcServiceBuilder evcUniBuilder =
+                new MseaUniEvcServiceOpParam.MseaUniEvcServiceBuilder();
+
+        MseaUniEvcServiceOpParam mseaUniEvcServiceFilter =
+                (MseaUniEvcServiceOpParam) evcUniBuilder.mefServices(mefServices).build();
+        try {
+            MseaUniEvcService uniEvcCurrent =
+                    mseaUniEvcServiceSvc.getConfigMseaUniEvcService(mseaUniEvcServiceFilter,
+                            session, TargetConfig.RUNNING);
+
+            flowEntryCollection.addAll(
+                    convertEvcUniToFlowRules(uniEvcCurrent, portAssignment));
+
+        } catch (NetconfException e) {
+            log.warn("Unexpected error on getFlowEntries", e);
+        }
+
+
+        return flowEntryCollection;
+    }
+
+    /**
+     * Apply the flow entries to the EA1000.
+     * Since the EA1000 does not have any 'real' flow entries these are converted 2 configuration
+     * areas on the EA1000 NETCONF model - to SA filtering YANG model and to EVC UNI YANG model.<br>
+     * Only a subset of the possible OpenFlow rules are supported. Any rule that's not handled
+     * will not be in the returned set.
+     *
+     * @param rules A collection of Flow Rules to be applied to the EA1000
+     * @return A collection of the Flow Rules that have been added.
+     */
+    @Override
+    public Collection<FlowRule> applyFlowRules(Collection<FlowRule> rules) {
+        Collection<FlowRule> frAdded = new HashSet<FlowRule>();
+        if (rules == null || rules.size() == 0) {
+            return rules;
+        }
+        NetconfController controller = checkNotNull(handler().get(NetconfController.class));
+        NetconfSession session = controller.getDevicesMap().get(handler().data().deviceId()).getSession();
+        MseaSaFilteringNetconfService mseaSaFilteringService =
+                (MseaSaFilteringNetconfService) checkNotNull(handler().get(MseaSaFilteringNetconfService.class));
+        MseaUniEvcServiceNetconfService mseaUniEvcServiceSvc =
+                (MseaUniEvcServiceNetconfService) checkNotNull(handler().get(MseaUniEvcServiceNetconfService.class));
+        log.debug("applyFlowRules() called on EA1000FlowRuleProgrammable with {} rules.", rules.size());
+        // FIXME: Change this so it's dynamically driven
+        UniSideInterfaceAssignmentEnum portAssignment = UniSideInterfaceAssignmentEnum.UNI_C_ON_HOST;
+
+        List<SourceAddressRange> saRangeList = new ArrayList<SourceAddressRange>();
+        Map<Integer, Evc> evcMap = new HashMap<>();
+
+        //Retrieve the list of actual EVCs and the CeVlanMaps from device
+        List<Evc> activeEvcs = new ArrayList<>();
+        try {
+            sessionMutex.acquire();
+            MseaUniEvcService evcResponse =
+                    mseaUniEvcServiceSvc.getmseaUniEvcCeVlanMaps(session, TargetConfig.RUNNING);
+            //There could be zero or more EVCs
+            if (evcResponse != null && evcResponse.mefServices() != null && evcResponse.mefServices().uni() != null) {
+                activeEvcs.addAll(evcResponse.mefServices().uni().evc());
+            }
+        } catch (NetconfException | InterruptedException e1) {
+            log.warn("Unexpected error on applyFlowRules", e1);
+        }
+
+        for (FlowRule fr : rules) {
+
+            // IP SA Filtering can only apply to Port 0 optics
+            if (fr.selector().getCriterion(Type.IPV4_SRC) != null &&
+                    fr.selector().getCriterion(Type.IN_PORT) != null &&
+                    ((PortCriterion) fr.selector().getCriterion(Type.IN_PORT)).port().toLong() == 0) {
+                parseFrForSaRange(frAdded, saRangeList, fr);
+
+            // EVCs will be defined by Flow Rules relating to VIDs
+            } else if (fr.selector().getCriterion(Type.VLAN_VID) != null &&
+                    fr.selector().getCriterion(Type.IN_PORT) != null) {
+                //There could be many Flow Rules for one EVC depending on the ceVlanMap
+                //Cannot build up the EVC until we know the details - the key is the tableID and port
+                parseFrForEvcs(frAdded, evcMap, activeEvcs, portAssignment, fr);
+            } else {
+                log.info("Unexpected Flow Rule type applied: " + fr);
+            }
+        }
+
+        //If there are IPv4 Flow Rules created commit them now through the
+        //MseaSaFiltering service
+        if (saRangeList.size() > 0) {
+            try {
+                mseaSaFilteringService.setMseaSaFiltering(
+                            buildSaFilteringObject(saRangeList), session, TargetConfig.RUNNING);
+            } catch (NetconfException e) {
+                log.error("Error applying Flow Rules to SA Filtering - will try again: " + e.getMessage());
+                sessionMutex.release();
+                return frAdded;
+            }
+        }
+        //If there are EVC flow rules then populate the MseaUniEvc part of EA1000
+        if (evcMap.size() > 0) {
+            List<Evc> evcList = evcMap.entrySet().stream()
+                    .map(x -> x.getValue())
+                    .collect(Collectors.toList());
+            Uni.UniBuilder uniBuilder = new DefaultUni.UniBuilder();
+            URI deviceName = handler().data().deviceId().uri();
+            Uni uni = uniBuilder.name(new Identifier45("Uni-on-"
+                    + deviceName.getSchemeSpecificPart())).evc(evcList).build();
+
+            List<BwpGroup> bwpGroupList = new ArrayList<BwpGroup>();
+            BwpGroup.BwpGroupBuilder bwpGrpBuilder = new DefaultBwpGroup.BwpGroupBuilder();
+            bwpGroupList.add(bwpGrpBuilder.groupIndex((short) 0).build());
+            Profiles profiles = (new DefaultProfiles.ProfilesBuilder()).bwpGroup(bwpGroupList).build();
+
+            MefServices.MefServicesBuilder mefBuilder = new DefaultMefServices.MefServicesBuilder();
+            MefServices mefServices = mefBuilder.uni(uni).profiles(profiles).build();
+
+            MseaUniEvcService.MseaUniEvcServiceBuilder evcUniBuilder =
+                    new MseaUniEvcServiceOpParam.MseaUniEvcServiceBuilder();
+
+            MseaUniEvcServiceOpParam mseaUniEvcServiceFilter =
+                    (MseaUniEvcServiceOpParam) evcUniBuilder.mefServices(mefServices).build();
+            try {
+                mseaUniEvcServiceSvc.setMseaUniEvcService(mseaUniEvcServiceFilter, session, TargetConfig.RUNNING);
+            } catch (NetconfException e) {
+                log.error("Error applying Flow Rules to EVC - will try again: " + e.getMessage());
+                sessionMutex.release();
+                return frAdded;
+            }
+        }
+        sessionMutex.release();
+        return frAdded;
+    }
+
+    /**
+     * Remove flow rules from the EA1000.
+     * Since the EA1000 does not have any 'real' flow entries these are converted 2 configuration
+     * areas on the EA1000 NETCONF model - to SA filtering YANG model and to EVC UNI YANG model.
+     *
+     * @param rulesToRemove A collection of Flow Rules to be removed to the EA1000
+     * @return A collection of the Flow Rules that have been removed.
+     */
+    @Override
+    public Collection<FlowRule> removeFlowRules(Collection<FlowRule> rulesToRemove) {
+        NetconfController controller = checkNotNull(handler().get(NetconfController.class));
+        NetconfSession session = controller.getDevicesMap().get(handler().data().deviceId()).getSession();
+        MseaSaFilteringNetconfService mseaSaFilteringService =
+                (MseaSaFilteringNetconfService) checkNotNull(handler().get(MseaSaFilteringNetconfService.class));
+        MseaUniEvcServiceNetconfService mseaUniEvcServiceSvc =
+                (MseaUniEvcServiceNetconfService) checkNotNull(handler().get(MseaUniEvcServiceNetconfService.class));
+        UniSideInterfaceAssignmentEnum portAssignment = UniSideInterfaceAssignmentEnum.UNI_C_ON_HOST;
+        log.debug("removeFlowRules() called on EA1000FlowRuleProgrammable with {} rules.", rulesToRemove.size());
+
+        if (rulesToRemove.size() == 0) {
+            return rulesToRemove;
+        }
+
+        //Retrieve the list of actual EVCs and the CeVlanMaps from device
+        List<Evc> activeEvcs = new ArrayList<>();
+        try {
+            sessionMutex.acquire();
+            MseaUniEvcService evcResponse =
+                    mseaUniEvcServiceSvc.getmseaUniEvcCeVlanMaps(session, TargetConfig.RUNNING);
+            //There could be zero or more EVCs
+            if (evcResponse != null && evcResponse.mefServices() != null && evcResponse.mefServices().uni() != null) {
+                activeEvcs.addAll(evcResponse.mefServices().uni().evc());
+            }
+        } catch (NetconfException | InterruptedException e1) {
+            log.warn("Error on removeFlowRules.", e1);
+        }
+
+        List<SourceAddressRange> saRangeList = new ArrayList<SourceAddressRange>();
+        Map<Integer, String> ceVlanMapMap = new HashMap<>();
+        Map<Integer, List<Short>> flowIdMap = new HashMap<>();
+
+        Collection<FlowRule> rulesRemoved = new HashSet<FlowRule>();
+        for (FlowRule ruleToRemove : rulesToRemove) {
+            // IP SA Filtering can only apply to Port 0 optics
+            if (ruleToRemove.selector().getCriterion(Type.IPV4_SRC) != null &&
+                    ruleToRemove.selector().getCriterion(Type.IN_PORT) != null &&
+                    ((PortCriterion) ruleToRemove.selector().getCriterion(Type.IN_PORT)).port().toLong() == 0) {
+                SourceAddressRange.SourceAddressRangeBuilder saBuilder =
+                        new DefaultSourceAddressRange.SourceAddressRangeBuilder();
+                SourceAddressRange sar = saBuilder
+                        .rangeId((short) ruleToRemove.tableId())
+                        .yangSourceAddressRangeOpType(org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.
+                                edge.assure.msea.sa.filtering.rev20160412.MseaSaFiltering.OnosYangOpType.DELETE)
+                        .build();
+
+                rulesRemoved.add(ruleToRemove);
+                saRangeList.add(sar);
+
+            } else if (ruleToRemove.selector().getCriterion(Type.VLAN_VID) != null &&
+                    ruleToRemove.selector().getCriterion(Type.IN_PORT) != null) {
+                PortNumber portNumber = ((PortCriterion) ruleToRemove.selector().getCriterion(Type.IN_PORT)).port();
+                VlanId vlanId = ((VlanIdCriterion) ruleToRemove.selector().getCriterion(Type.VLAN_VID)).vlanId();
+                int evcId = ruleToRemove.tableId();
+                int evcKey = (evcId << 2) + (int) portNumber.toLong();
+                String activeCeVlanMap = "";
+                //If this is one of many VLANs belonging to an EVC then we should only remove this VLAN
+                // from the ceVlanMap and not the whole EVC
+                if (!ceVlanMapMap.containsKey(evcKey)) {
+                    for (Evc activeEvc:activeEvcs) {
+                        if (activeEvc.evcIndex() == evcId) {
+                            if (Ea1000Port.fromNum(portNumber.toLong()).nOrC(portAssignment) ==
+                                    UniSide.CUSTOMER) {
+                                activeCeVlanMap = activeEvc.evcPerUni().evcPerUnic().ceVlanMap().string();
+                            } else if (Ea1000Port.fromNum(portNumber.toLong()).nOrC(portAssignment) ==
+                                    UniSide.NETWORK) {
+                                activeCeVlanMap = activeEvc.evcPerUni().evcPerUnin().ceVlanMap().string();
+                            }
+                        }
+                    }
+                }
+
+                ceVlanMapMap.put(evcKey, CeVlanMapUtils.removeFromCeVlanMap(activeCeVlanMap, vlanId.id()));
+                if (!flowIdMap.containsKey(evcKey)) {
+                    flowIdMap.put(evcKey, new ArrayList<>());
+                }
+                flowIdMap.get(evcKey).add(vlanId.id());
+                rulesRemoved.add(ruleToRemove);
+
+            } else {
+                log.info("Unexpected Flow Rule type removal: " + ruleToRemove);
+            }
+        }
+
+        //If there are IPv4 Flow Rules created commit them now through the
+        //MseaSaFiltering service
+        if (saRangeList.size() > 0) {
+            try {
+                mseaSaFilteringService.setMseaSaFiltering(
+                        buildSaFilteringObject(saRangeList), session, TargetConfig.RUNNING);
+            } catch (NetconfException e) {
+                log.warn("Remove FlowRule on MseaSaFilteringService could not delete SARule - "
+                        + "it may already have been deleted: " + e.getMessage());
+            }
+        }
+
+        if (ceVlanMapMap.size() > 0) {
+            try {
+                mseaUniEvcServiceSvc.removeEvcUniFlowEntries(ceVlanMapMap, flowIdMap,
+                        session, TargetConfig.RUNNING, portAssignment);
+            } catch (NetconfException e) {
+                log.warn("Remove FlowRule on MseaUniEvcService could not delete EVC - "
+                        + "it may already have been deleted: " + e.getMessage());
+            }
+        }
+
+        sessionMutex.release();
+        return rulesRemoved;
+    }
+
+    /**
+     * An internal method for extracting one EVC from a list and returning its ceVlanMap.
+     *
+     * @param evcList - the list of known EVCs
+     * @param evcIndex - the index of the EVC we're looking for
+     * @param side - the side of the UNI
+     * @return - the CEVlanMap we're looking for
+     */
+    private String getCeVlanMapForIdxFromEvcList(List<Evc> evcList, long evcIndex, UniSide side) {
+        if (evcList != null && evcList.size() > 0) {
+            for (Evc evc:evcList) {
+                if (evc.evcIndex() == evcIndex && evc.evcPerUni() != null) {
+                    if (side == UniSide.CUSTOMER &&
+                        evc.evcPerUni().evcPerUnic() != null &&
+                        evc.evcPerUni().evcPerUnic().ceVlanMap() != null) {
+                        return evc.evcPerUni().evcPerUnic().ceVlanMap().string();
+                    } else if (side == UniSide.NETWORK &&
+                        evc.evcPerUni().evcPerUnin() != null &&
+                        evc.evcPerUni().evcPerUnin().ceVlanMap() != null) {
+                        return evc.evcPerUni().evcPerUnin().ceVlanMap().string();
+                    }
+                }
+            }
+        }
+
+        return ""; //The EVC required was not in the list
+    }
+
+    /**
+     * An internal method to convert from a FlowRule to SARange.
+     *
+     * @param frList A collection of flow rules
+     * @param saRangeList A list of SARanges
+     * @param fr A flow rule
+     */
+    private void parseFrForSaRange(Collection<FlowRule> frList, List<SourceAddressRange> saRangeList, FlowRule fr) {
+        String ipAddrStr = fr.selector().getCriterion(Type.IPV4_SRC).toString().substring(9);
+        log.debug("Applying IP address to " + ipAddrStr
+                + " (on Port 0) to IP SA Filtering on EA1000 through NETCONF");
+
+        SourceAddressRange.SourceAddressRangeBuilder saBuilder =
+                new DefaultSourceAddressRange.SourceAddressRangeBuilder();
+
+        SourceAddressRange sar = saBuilder
+                .rangeId((short) fr.tableId())
+                .name("Flow:" + fr.id().toString())
+                .ipv4AddressPrefix(ipAddrStr)
+                .build();
+
+        frList.add(fr);
+        saRangeList.add(sar);
+    }
+
+    private void parseFrForEvcs(Collection<FlowRule> frList, Map<Integer, Evc> evcMap,
+            List<Evc> activeEvcs, UniSideInterfaceAssignmentEnum portAssignment, FlowRule fr) {
+        //There could be many Flow Rules for one EVC depending on the ceVlanMap
+        //Cannot build up the EVC until we know the details - the key is the tableID and port
+        Ea1000Port port = Ea1000Port.fromNum(
+                ((PortCriterion) fr.selector().getCriterion(Type.IN_PORT)).port().toLong());
+        Integer evcKey = (fr.tableId() << 2) + port.portNum();
+        VlanId sourceVid = ((VlanIdCriterion) fr.selector().getCriterion(Type.VLAN_VID)).vlanId();
+        FlowMapping.FlowMappingBuilder fmBuilder =
+                DefaultFlowMapping.builder()
+                .ceVlanId(VlanIdType.of(sourceVid.id()))
+                .flowId(BigInteger.valueOf(fr.id().value()));
+        if (evcMap.containsKey(evcKey)) { //Is there an entry already for this EVC and port?
+            //Replace ceVlanMap
+            evcMap.put(evcKey, CustomEvc.builder(evcMap.get(evcKey))
+                    .addToCeVlanMap(new ServiceListType(sourceVid.toString()), port.nOrC(portAssignment))
+                    .addToFlowMapping(fmBuilder.build(), port.nOrC(portAssignment))
+                    .build());
+
+        } else if (evcMap.containsKey((evcKey ^ 1))) { //Is there an entry for this EVC but the opposite port?
+            TagManipulation tm = getTagManipulation(fr);
+            if (port.nOrC(portAssignment) == UniSide.NETWORK) {
+                EvcPerUnin epun = CustomEvcPerUnin.builder(evcMap.get(evcKey ^ 1).evcPerUni().evcPerUnin())
+                        .addToCeVlanMap(new ServiceListType(sourceVid.toString()))
+                        .tagManipulation(tm)
+                        .addToFlowMapping(fmBuilder.build())
+                        .ingressBwpGroupIndex(getMeterId(fr.treatment()))
+                        .build();
+                evcMap.put((evcKey ^ 1), CustomEvc.builder(evcMap.get((evcKey ^ 1))).addUniN(epun).build());
+            } else {
+                EvcPerUnic epuc = CustomEvcPerUnic.builder(evcMap.get(evcKey ^ 1).evcPerUni().evcPerUnic())
+                        .addToCeVlanMap(new ServiceListType(sourceVid.toString()))
+                        .tagManipulation(tm)
+                        .addToFlowMapping(fmBuilder.build())
+                        .ingressBwpGroupIndex(getMeterId(fr.treatment()))
+                        .build();
+                evcMap.put((evcKey ^ 1), CustomEvc.builder(evcMap.get((evcKey ^ 1))).addUniC(epuc).build());
+            }
+        } else {
+            Evc.EvcBuilder evcBuilder = new DefaultEvc.EvcBuilder();
+            EvcPerUninBuilder epunBuilder = new CustomEvcPerUnin.EvcPerUninBuilder();
+            EvcPerUnicBuilder epucBuilder = new CustomEvcPerUnic.EvcPerUnicBuilder();
+            TagManipulation tm = getTagManipulation(fr);
+
+            UniSide side = port.nOrC(portAssignment);
+            String oldCeVlanMap = getCeVlanMapForIdxFromEvcList(activeEvcs, fr.tableId(), side);
+            String newCeVlanMap =
+                    CeVlanMapUtils.addtoCeVlanMap(oldCeVlanMap, sourceVid.id());
+            String oppositeCeVlanMap =
+                    getCeVlanMapForIdxFromEvcList(activeEvcs, fr.tableId(),
+                            port.opposite().nOrC(portAssignment));
+            oppositeCeVlanMap = oppositeCeVlanMap.isEmpty() ? "0" : oppositeCeVlanMap;
+            if (side == UniSide.NETWORK) {
+                epunBuilder
+                    .ceVlanMap(new ServiceListType(newCeVlanMap))
+                    .tagManipulation(tm)
+                    .addToFlowMapping(fmBuilder.build())
+                    .ingressBwpGroupIndex(getMeterId(fr.treatment()));
+
+                epucBuilder.ceVlanMap(new ServiceListType(oppositeCeVlanMap));
+            } else {
+                epucBuilder
+                    .ceVlanMap(new ServiceListType(newCeVlanMap))
+                    .tagManipulation(tm)
+                    .addToFlowMapping(fmBuilder.build())
+                    .ingressBwpGroupIndex(getMeterId(fr.treatment()));
+
+                epunBuilder.ceVlanMap(new ServiceListType(oppositeCeVlanMap));
+            }
+
+            evcBuilder
+                    .evcIndex(fr.tableId())
+                    .name(new Identifier45("EVC-" + String.valueOf(fr.tableId())))
+                    .evcPerUni(new DefaultEvcPerUni.EvcPerUniBuilder()
+                            .evcPerUnin(epunBuilder.build())
+                            .evcPerUnic(epucBuilder.build())
+                            .build());
+            evcMap.put(evcKey, evcBuilder.build());
+        }
+
+        frList.add(fr);
+    }
+
+
+    private MseaSaFilteringOpParam buildSaFilteringObject(List<SourceAddressRange> saRangeList) {
+        InterfaceEth0.InterfaceEth0Builder ifBuilder = new DefaultInterfaceEth0.InterfaceEth0Builder();
+        for (SourceAddressRange sa:saRangeList) {
+            ifBuilder = ifBuilder.addToSourceAddressRange(sa);
+        }
+        InterfaceEth0 saIf = ifBuilder.filterAdminState(FilterAdminStateEnum.BLACKLIST).build();
+
+        SourceIpaddressFiltering.SourceIpaddressFilteringBuilder saFilterBuilder =
+                new DefaultSourceIpaddressFiltering.SourceIpaddressFilteringBuilder();
+        SourceIpaddressFiltering saFilter = saFilterBuilder.interfaceEth0(saIf).build();
+
+        MseaSaFilteringOpParam.MseaSaFilteringBuilder opBuilder =
+                new MseaSaFilteringOpParam.MseaSaFilteringBuilder();
+        MseaSaFilteringOpParam mseaSaFiltering =
+                (MseaSaFilteringOpParam) opBuilder.sourceIpaddressFiltering(saFilter).build();
+
+        return mseaSaFiltering;
+    }
+
+    private Collection<FlowEntry> convertSaFilteringToFlowRules(
+            MseaSaFiltering saFilteringCurrent, ApplicationId appId) {
+        Collection<FlowEntry> flowEntryCollection = new HashSet<FlowEntry>();
+
+        List<SourceAddressRange> saRangelist =
+                saFilteringCurrent.sourceIpaddressFiltering().interfaceEth0().sourceAddressRange();
+        Criterion matchInPort = Criteria.matchInPort(PortNumber.portNumber(0));
+        TrafficSelector.Builder tsBuilder = DefaultTrafficSelector.builder();
+
+        for (SourceAddressRange sa:saRangelist) {
+            Criterion matchIpSrc = Criteria.matchIPSrc(IpPrefix.valueOf(sa.ipv4AddressPrefix()));
+
+            TrafficSelector selector = tsBuilder.add(matchIpSrc).add(matchInPort).build();
+
+            TrafficTreatment.Builder trBuilder = DefaultTrafficTreatment.builder();
+            TrafficTreatment treatment = trBuilder.drop().build();
+
+            FlowRule.Builder feBuilder = new DefaultFlowRule.Builder();
+            if (sa.name() != null && sa.name().startsWith("Flow:")) {
+                String[] nameParts = sa.name().split(":");
+                Long cookie = Long.valueOf(nameParts[1], 16);
+                feBuilder = feBuilder.withCookie(cookie);
+            } else {
+                feBuilder = feBuilder.fromApp(appId);
+            }
+
+            FlowRule fr = feBuilder
+                .forDevice(handler().data().deviceId())
+                .withSelector(selector)
+                .withTreatment(treatment)
+                .forTable(sa.rangeId())
+                .makePermanent()
+                .withPriority(PRIORITY_DEFAULT)
+                .build();
+
+            flowEntryCollection.add(new DefaultFlowEntry(fr, FlowEntryState.ADDED, 0, 0, 0));
+        }
+
+        return flowEntryCollection;
+    }
+
+
+    private Collection<FlowEntry> convertEvcUniToFlowRules(
+            MseaUniEvcService uniEvcCurrent, UniSideInterfaceAssignmentEnum portAssignment) {
+        Collection<FlowEntry> flowEntryCollection = new HashSet<FlowEntry>();
+
+        if (uniEvcCurrent == null || uniEvcCurrent.mefServices() == null ||
+                uniEvcCurrent.mefServices().uni() == null || uniEvcCurrent.mefServices().uni().evc() == null) {
+            log.info("No EVC's found when getting flow rules");
+            return flowEntryCollection;
+        }
+
+        for (Evc evc:uniEvcCurrent.mefServices().uni().evc()) {
+            FlowRule.Builder frBuilder = new DefaultFlowRule.Builder();
+            TrafficSelector.Builder tsBuilder = DefaultTrafficSelector.builder();
+
+            TrafficTreatment uniNTreatment = treatmentForUniSde(evc.evcPerUni(), true);
+            //Depending on the ceVlanMap there may be multiple VLans and hence multiple flow entries
+            Short[] vlanIdsUniN =
+                    CeVlanMapUtils.getVlanSet(ceVlanMapForUniSide(evc.evcPerUni(), true));
+            for (Short vlanId:vlanIdsUniN) {
+                if (vlanId == 0) {
+                    continue;
+                }
+                Criterion uniNportCriterion = criterionPortForUniSide(portAssignment, true);
+                TrafficSelector tsUniN = tsBuilder.matchVlanId(VlanId.vlanId(vlanId)).add(uniNportCriterion).build();
+                long flowId = getFlowIdForVlan(evc.evcPerUni().evcPerUnin().flowMapping(), vlanId);
+
+                FlowRule frUniN = frBuilder
+                    .forDevice(handler().data().deviceId())
+                    .withSelector(tsUniN)
+                    .withTreatment(uniNTreatment)
+                    .forTable(new Long(evc.evcIndex()).intValue()) //narrowing to int
+                    .makePermanent()
+                    .withPriority(PRIORITY_DEFAULT)
+                    .withCookie(flowId)
+                    .build();
+                flowEntryCollection.add(new DefaultFlowEntry(frUniN, FlowEntryState.ADDED, 0, 0, 0));
+            }
+
+            TrafficTreatment uniCTreatment = treatmentForUniSde(evc.evcPerUni(), false);
+            //Depending on the ceVlanMap there may be multiple VLans and hence multiple flow entries
+            Short[] vlanIdsUniC =
+                    CeVlanMapUtils.getVlanSet(ceVlanMapForUniSide(evc.evcPerUni(), false));
+            if (vlanIdsUniC != null && vlanIdsUniC.length > 0) {
+                for (Short vlanId:vlanIdsUniC) {
+                    if (vlanId == 0) {
+                        continue;
+                    }
+                    Criterion uniCportCriterion = criterionPortForUniSide(portAssignment, false);
+                    TrafficSelector tsUniC =
+                            tsBuilder.matchVlanId(VlanId.vlanId(vlanId)).add(uniCportCriterion).build();
+                    long flowId = getFlowIdForVlan(evc.evcPerUni().evcPerUnic().flowMapping(), vlanId);
+
+                    FlowRule frUniC = frBuilder
+                            .forDevice(handler().data().deviceId())
+                            .withSelector(tsUniC)
+                            .withTreatment(uniCTreatment)
+                            .forTable(new Long(evc.evcIndex()).intValue()) //narrowing to int
+                            .makePermanent()
+                            .withPriority(PRIORITY_DEFAULT)
+                            .withCookie(flowId)
+                            .build();
+                    flowEntryCollection.add(new DefaultFlowEntry(frUniC, FlowEntryState.ADDED, 0, 0, 0));
+                }
+            }
+        }
+
+        return flowEntryCollection;
+    }
+
+    private long getFlowIdForVlan(List<FlowMapping> fmList, Short vlanId) {
+        if (fmList == null || vlanId == null) {
+            log.warn("Flow Mapping list is null when reading EVCs");
+            return -1L;
+        }
+        for (FlowMapping fm:fmList) {
+            if (fm.ceVlanId().uint16() == vlanId.intValue()) {
+                return fm.flowId().longValue();
+            }
+        }
+        return 0L;
+    }
+
+    private String ceVlanMapForUniSide(
+            EvcPerUni evcPerUni, boolean portN) {
+        if (portN) {
+            return evcPerUni.evcPerUnin().ceVlanMap().string();
+        } else {
+            return evcPerUni.evcPerUnic().ceVlanMap().string();
+        }
+    }
+
+    private Criterion criterionPortForUniSide(
+            UniSideInterfaceAssignmentEnum portAssignment, boolean portN) {
+        boolean cOnOptics = (portAssignment == UniSideInterfaceAssignmentEnum.UNI_C_ON_OPTICS);
+        int portNum = ((cOnOptics && portN) || (!cOnOptics && !portN)) ? 1 : 0;
+        return Criteria.matchInPort(PortNumber.portNumber(portNum));
+    }
+
+    private TrafficTreatment treatmentForUniSde(
+            EvcPerUni evcPerUni, boolean portN) {
+        TrafficTreatment.Builder trBuilder = DefaultTrafficTreatment.builder();
+
+        TagManipulation tm = null;
+        short meterId = 0;
+        if (portN) {
+            tm = evcPerUni.evcPerUnin().tagManipulation();
+            meterId = (short) evcPerUni.evcPerUnin().ingressBwpGroupIndex();
+        } else {
+            tm = evcPerUni.evcPerUnic().tagManipulation();
+            meterId = (short) evcPerUni.evcPerUnic().ingressBwpGroupIndex();
+        }
+
+        if (meterId > 0L) {
+            trBuilder = trBuilder.meter(MeterId.meterId((long) meterId));
+//            trBuilder = trBuilder.meter(MeterId.meterId(meterId)).transition(0);
+        }
+
+        if (tm == null) {
+            return trBuilder.build(); //no tag manipulation found
+        }
+
+        if (tm.getClass().equals(DefaultTagPush.class)) {
+            VlanId pushVlanNum = VlanId.vlanId((short) ((TagPush) tm).tagPush().outerTagVlan().uint16());
+            PushTagTypeEnum pushTagType = ((TagPush) tm).tagPush().pushTagType();
+            //Note - the order of elements below MUST match the order of the Treatment in the stored FlowRule
+            // to be an exactMatch. See DefaultFlowRule.exactMatch()
+            trBuilder = trBuilder
+                    .pushVlan(pushTagType.equals(PushTagTypeEnum.PUSHCTAG) ?
+                            EtherType.VLAN.ethType() : EtherType.QINQ.ethType())
+                    .setVlanId(pushVlanNum).transition(Integer.valueOf(0));
+
+        } else if (tm.getClass().equals(DefaultTagPop.class)) {
+            trBuilder = trBuilder.popVlan();
+
+        } else if (tm.getClass().equals(DefaultTagOverwrite.class)) {
+            TagOverwrite to = (TagOverwrite) tm;
+            VlanId ovrVlanNum = VlanId
+                    .vlanId((short) (
+                            //There are 2 classes TagOverwrite - the other one is already imported
+                            to
+                            .tagOverwrite()
+                            .outerTagVlan()
+                            .uint16()));
+            trBuilder = trBuilder.setVlanId(ovrVlanNum);
+
+        }
+
+        return trBuilder.build();
+    }
+
+    private static TagManipulation getTagManipulation(FlowRule fr) {
+        boolean isPop = false;
+        boolean isPush = false;
+        VlanId vlanId = null;
+        EthType ethType = EtherType.VLAN.ethType(); //Default
+        for (Instruction inst:fr.treatment().allInstructions()) {
+            if (inst.type() == Instruction.Type.L2MODIFICATION) {
+                L2ModificationInstruction l2Mod = (L2ModificationInstruction) inst;
+                if (l2Mod.subtype() == L2ModificationInstruction.L2SubType.VLAN_POP) {
+                    isPop = true;
+                } else if (l2Mod.subtype() == L2ModificationInstruction.L2SubType.VLAN_PUSH) {
+                    isPush = true;
+                    ethType = ((ModVlanHeaderInstruction) l2Mod).ethernetType();
+                } else if (l2Mod.subtype() == L2ModificationInstruction.L2SubType.VLAN_ID) {
+                    vlanId = ((ModVlanIdInstruction) l2Mod).vlanId();
+                }
+            }
+        }
+
+        if (isPop) {
+            //The should be no vlanId in this case
+            TagPop.TagPopBuilder popBuilder = new DefaultTagPop.TagPopBuilder();
+            org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc
+                .service.rev20160317.mseaunievcservice.evcperuniextensionattributes.tagmanipulation
+                .tagpop.TagPop.TagPopBuilder popInnerBuilder =
+                    new org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea
+                        .uni.evc.service.rev20160317.mseaunievcservice.evcperuniextensionattributes
+                        .tagmanipulation.tagpop.DefaultTagPop.TagPopBuilder();
+            return popBuilder
+                    .tagPop(popInnerBuilder.build())
+                    .build();
+
+        } else if (isPush && vlanId != null) {
+            TagPush.TagPushBuilder pushBuilder = new DefaultTagPush.TagPushBuilder();
+            org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc
+                .service.rev20160317.mseaunievcservice.evcperuniextensionattributes.tagmanipulation
+                .tagpush.TagPush.TagPushBuilder pushInnerBuilder =
+                    new org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea
+                        .uni.evc.service.rev20160317.mseaunievcservice.evcperuniextensionattributes
+                        .tagmanipulation.tagpush.DefaultTagPush.TagPushBuilder();
+            return pushBuilder
+                .tagPush(pushInnerBuilder
+                        .outerTagVlan(new VlanIdType(vlanId.id()))
+                        .pushTagType(ethType.equals(EtherType.VLAN.ethType()) ?
+                                PushTagTypeEnum.PUSHCTAG : PushTagTypeEnum.PUSHSTAG)
+                        .build())
+                .build();
+
+        } else if (vlanId != null) { //This is overwrite, as it has vlanId, but not push or pop
+            TagOverwrite.TagOverwriteBuilder ovrBuilder = new DefaultTagOverwrite.TagOverwriteBuilder();
+            org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc
+                .service.rev20160317.mseaunievcservice.evcperuniextensionattributes.tagmanipulation
+                .tagoverwrite.TagOverwrite.TagOverwriteBuilder ovrInnerBuilder =
+                    new org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea
+                        .uni.evc.service.rev20160317.mseaunievcservice.evcperuniextensionattributes
+                        .tagmanipulation.tagoverwrite.DefaultTagOverwrite.TagOverwriteBuilder();
+            return ovrBuilder.
+                    tagOverwrite(ovrInnerBuilder
+                            .outerTagVlan(new VlanIdType(vlanId.id()))
+                            .build())
+                    .build();
+        }
+
+        return null;
+    }
+
+    private static long getMeterId(TrafficTreatment treatment) {
+        return (treatment.metered() != null && treatment.metered().meterId() != null)
+                ? treatment.metered().meterId().id() : 0L;
+    }
+
+    /**
+     * An enumerated type that characterises the 2 port layout of the EA1000 device.
+     * The device is in an SFP package and has only 2 ports, the HOST port which
+     * plugs in to the chassis (Port 1) and the Optics Port on the rear (Port 0).
+     */
+    public enum Ea1000Port {
+        HOST(1),
+        OPTICS(0);
+
+        private int num = 0;
+        private Ea1000Port(int num) {
+            this.num = num;
+        }
+
+        /**
+         * The numerical assignment of this port.
+         * @return The port number
+         */
+        public int portNum() {
+            return num;
+        }
+
+        /**
+         * Return the enumerated value from a port number.
+         * @param num The port number
+         * @return An enumerated value
+         */
+        public static Ea1000Port fromNum(long num) {
+            for (Ea1000Port a:Ea1000Port.values()) {
+                if (a.num == num) {
+                    return a;
+                }
+            }
+            return HOST;
+        }
+
+        /**
+         * Get the port that the UNI-N is present on.
+         * @param side The assignment of UNI-side to port
+         * @return An enumerated value
+         */
+        public static Ea1000Port uniNNum(UniSideInterfaceAssignmentEnum side) {
+            if (side.equals(UniSideInterfaceAssignmentEnum.UNI_C_ON_HOST)) {
+                return OPTICS;
+            } else {
+                return HOST;
+            }
+        }
+
+        /**
+         * Get the port that the UNI-C is present on.
+         * @param side The assignment of UNI-side to port
+         * @return An enumerated value
+         */
+        public static Ea1000Port uniCNum(UniSideInterfaceAssignmentEnum side) {
+            if (side.equals(UniSideInterfaceAssignmentEnum.UNI_C_ON_HOST)) {
+                return HOST;
+            } else {
+                return OPTICS;
+            }
+        }
+
+        /**
+         * Get the port opposite the current port.
+         * @return An enumerated value for the opposite side
+         */
+        public Ea1000Port opposite() {
+            if (this.equals(HOST)) {
+                return OPTICS;
+            } else {
+                return HOST;
+            }
+        }
+
+        /**
+         * Evaluate which side of the UNI on the EA1000 device this port refers to.
+         * @param side The assignment of UNI-side to port
+         * @return An enumerated value representing the UniSide
+         */
+        public UniSide nOrC(UniSideInterfaceAssignmentEnum side) {
+            if ((this == HOST && side == UniSideInterfaceAssignmentEnum.UNI_C_ON_HOST) ||
+                    (this == OPTICS && side == UniSideInterfaceAssignmentEnum.UNI_C_ON_OPTICS)) {
+                return UniSide.CUSTOMER;
+            } else {
+                return UniSide.NETWORK;
+            }
+        }
+    }
+}
diff --git a/drivers/microsemi/ea1000driver/src/main/java/org/onosproject/drivers/microsemi/EA1000MeterProvider.java b/drivers/microsemi/ea1000driver/src/main/java/org/onosproject/drivers/microsemi/EA1000MeterProvider.java
new file mode 100644
index 0000000..2ecc70f
--- /dev/null
+++ b/drivers/microsemi/ea1000driver/src/main/java/org/onosproject/drivers/microsemi/EA1000MeterProvider.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.drivers.microsemi;
+
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.core.CoreService;
+import org.onosproject.drivers.microsemi.yang.MseaUniEvcServiceNetconfService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.meter.Band;
+import org.onosproject.net.meter.Meter.Unit;
+import org.onosproject.net.meter.MeterOperation;
+import org.onosproject.net.meter.MeterOperations;
+import org.onosproject.net.meter.MeterProvider;
+import org.onosproject.net.meter.MeterProviderRegistry;
+import org.onosproject.net.meter.MeterProviderService;
+import org.onosproject.net.provider.AbstractProvider;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.netconf.NetconfController;
+import org.onosproject.netconf.NetconfException;
+import org.onosproject.netconf.NetconfSession;
+import org.onosproject.netconf.TargetConfig;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.types.rev20160229.mseatypes.CosColorType;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.types.rev20160229.mseatypes.PriorityType;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.types.rev20160229.mseatypes.coscolortype.CosColorTypeEnum;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.MseaUniEvcService;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.MseaUniEvcService.OnosYangOpType;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.MseaUniEvcServiceOpParam;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.DefaultMefServices;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.MefServices;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.DefaultProfiles;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.DefaultProfiles.ProfilesBuilder;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.profiles.BwpGroup;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.profiles.Cos;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.profiles.DefaultBwpGroup;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.profiles.DefaultCos;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.profiles.bwpgroup.Bwp;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.profiles.bwpgroup.DefaultBwp;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.profiles.cos.costypechoice.DefaultEvcCosTypeEvcColorId;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.profiles.cos.costypechoice.evccostypeevccolorid.DefaultEvcCosTypeAll8PrioTo1EvcColor;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.uni.evc.service.rev20160317.mseaunievcservice.mefservices.profiles.cos.costypechoice.evccostypeevccolorid.EvcCosTypeAll8PrioTo1EvcColor;
+import org.slf4j.Logger;
+
+/**
+ * Provider which uses an NETCONF controller to handle meters.
+ */
+@Component(immediate = true, enabled = true)
+public class EA1000MeterProvider extends AbstractProvider implements MeterProvider {
+
+    private final Logger log = getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected NetconfController controller;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected MeterProviderRegistry providerRegistry;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected MseaUniEvcServiceNetconfService mseaUniEvcServiceSvc;
+
+    private MeterProviderService providerService;
+
+    private static final int COS_INDEX_1 = 1;
+    private static final short DEFAULT_OUTGOING_PRIO = 3;
+
+    /**
+     * Creates a OpenFlow meter provider.
+     */
+    public EA1000MeterProvider() {
+        super(new ProviderId("netconf", "org.onosproject.provider.meter.microsemi"));
+    }
+
+    @Activate
+    public void activate() {
+        providerService = providerRegistry.register(this);
+
+    }
+
+    @Deactivate
+    public void deactivate() {
+        providerRegistry.unregister(this);
+
+        providerService = null;
+    }
+
+    @Override
+    public void performMeterOperation(DeviceId deviceId, MeterOperations meterOps) {
+        log.debug("Adding meterOps to Microsemi Meter Store");
+    }
+
+    @Override
+    public void performMeterOperation(DeviceId deviceId, MeterOperation meterOp) {
+        if (meterOp == null || deviceId == null) {
+            log.warn("Missing argument for performMeterOperation()");
+            return;
+        }
+        log.debug("{} meterOp {} to Microsemi Meter Store", meterOp.type(), meterOp);
+
+        long meterId = meterOp.meter().id().id();
+        String deviceName = deviceId.uri().getSchemeSpecificPart();
+        Unit unit = meterOp.meter().unit();
+
+        ProfilesBuilder profilesBuilder = DefaultProfiles.builder();
+        if (meterOp.type() == MeterOperation.Type.ADD || meterOp.type() == MeterOperation.Type.MODIFY) {
+            Bwp.BwpBuilder bwpBuilder = DefaultBwp.builder()
+                    .cosIndex(COS_INDEX_1)
+                    .name("BWP-" + String.valueOf(meterId) + "-" + deviceName);
+
+            long cirRateKbps = 0L;
+            long cbsRateKbps = 0L;
+            long eirRateKbps = 0L;
+            long ebsRateKbps = 0L;
+            for (Band band:meterOp.meter().bands()) {
+                if (band.type() == Band.Type.REMARK) {
+                    //This relates to CIR/CBS
+                    cirRateKbps = toBitsPerSec(band.rate(), unit);
+                    cbsRateKbps = band.burst(); //Already in kbps
+                } else if (band.type() == Band.Type.DROP) {
+                    //This relates to EIR/EBS
+                    eirRateKbps = toBitsPerSec(band.rate(), unit);
+                    ebsRateKbps = band.burst(); //Already in kbps
+                }
+            }
+            bwpBuilder.committedInformationRate(cirRateKbps).excessInformationRate(eirRateKbps - cirRateKbps);
+            if (meterOp.meter().isBurst()) {
+                bwpBuilder.committedBurstSize(cbsRateKbps).excessBurstSize(ebsRateKbps - cbsRateKbps);
+            }
+
+            BwpGroup.BwpGroupBuilder bwpgBuilder =
+                    DefaultBwpGroup.builder()
+                    .groupIndex((short) meterId)
+                    .addToBwp(bwpBuilder.build());
+
+            //Create cos-1 as referenced above - we only support 1 at the moment
+            Cos.CosBuilder cosBuilder = DefaultCos.builder()
+                    .cosIndex(COS_INDEX_1)
+                    .name("COS-1")
+                    .outgoingCosValue(PriorityType.of(DEFAULT_OUTGOING_PRIO))
+                    .colorAware(true)
+                    .colorForward(true);
+            EvcCosTypeAll8PrioTo1EvcColor ect =
+                    DefaultEvcCosTypeAll8PrioTo1EvcColor.builder()
+                        .evcAll8ColorTo(CosColorType.of(CosColorTypeEnum.GREEN)).build();
+            profilesBuilder
+                    .addToBwpGroup(bwpgBuilder.build())
+                    .addToCos(cosBuilder.cosTypeChoice(
+                                    DefaultEvcCosTypeEvcColorId.builder()
+                                    .evcCosTypeAll8PrioTo1EvcColor(ect).build()).build())
+                    .build();
+        } else if (meterOp.type() == MeterOperation.Type.REMOVE) {
+            BwpGroup.BwpGroupBuilder bwpgBuilder =
+                    DefaultBwpGroup.builder()
+                    .groupIndex((short) meterId)
+                    .yangBwpGroupOpType(OnosYangOpType.DELETE);
+
+            profilesBuilder.addToBwpGroup(bwpgBuilder.build()).build();
+        }
+
+        MefServices mefServices = DefaultMefServices.builder().profiles(profilesBuilder.build()).build();
+
+        MseaUniEvcService.MseaUniEvcServiceBuilder evcUniBuilder =
+                new MseaUniEvcServiceOpParam.MseaUniEvcServiceBuilder();
+
+        MseaUniEvcServiceOpParam mseaUniEvcServiceFilter =
+                (MseaUniEvcServiceOpParam) evcUniBuilder.mefServices(mefServices).build();
+        NetconfSession session = controller.getDevicesMap().get(deviceId).getSession();
+        try {
+            mseaUniEvcServiceSvc.setMseaUniEvcService(mseaUniEvcServiceFilter, session, TargetConfig.RUNNING);
+        } catch (NetconfException e) {
+            //This can fail if the BWP Group is deleted before the EVC that is dependent on it
+            //The delete of the EVC will be called on a separate thread to that should proceed
+            //within a few seconds after which we should try again
+            AtomicInteger retry = new AtomicInteger(4);
+            if (meterOp.type() == MeterOperation.Type.REMOVE &&
+                    e.getMessage().startsWith("Failed to run edit-config through NETCONF")) {
+                while (retry.getAndDecrement() > 0) {
+                    try {
+                        Thread.sleep(1000L);
+                        log.debug("Retrying deletion of Bandwith Profile Group {}", String.valueOf(meterId));
+                        mseaUniEvcServiceSvc.setMseaUniEvcService(mseaUniEvcServiceFilter,
+                                session, TargetConfig.RUNNING);
+                        return; //If it did not throw an exception
+                    } catch (InterruptedException e1) {
+                        // TODO Auto-generated catch block
+                        e1.printStackTrace();
+                    } catch (NetconfException e1) {
+                        log.debug("NETCONF failed to delete profile - trying again in 1 sec");
+                        e1.printStackTrace();
+                    }
+                }
+                log.error("Error deleting BWPGroup {} from {} after 4 tries: {}", meterId, deviceId, e.getMessage());
+            } else {
+                log.error("Error adding BWPGroup {} from {}: {}", meterId, deviceId, e.getMessage());
+                throw new UnsupportedOperationException(e);
+            }
+            e.printStackTrace();
+        }
+    }
+
+    private static long toBitsPerSec(long rate, Unit unit) {
+        if (unit == Unit.KB_PER_SEC) {
+            return rate * 8;
+        } else {
+            return -1;
+        }
+    }
+}
diff --git a/drivers/microsemi/ea1000driver/src/main/java/org/onosproject/drivers/microsemi/EA1000Pipeliner.java b/drivers/microsemi/ea1000driver/src/main/java/org/onosproject/drivers/microsemi/EA1000Pipeliner.java
new file mode 100644
index 0000000..308bdff
--- /dev/null
+++ b/drivers/microsemi/ea1000driver/src/main/java/org/onosproject/drivers/microsemi/EA1000Pipeliner.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.drivers.microsemi;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import org.onlab.osgi.ServiceDirectory;
+import org.onosproject.drivers.microsemi.EA1000FlowRuleProgrammable.Ea1000Port;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.NextGroup;
+import org.onosproject.net.behaviour.Pipeliner;
+import org.onosproject.net.behaviour.PipelinerContext;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleOperations;
+import org.onosproject.net.flow.FlowRuleOperationsContext;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.Criterion.Type;
+import org.onosproject.net.flow.criteria.PortCriterion;
+import org.onosproject.net.flow.criteria.VlanIdCriterion;
+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.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
+import org.onosproject.net.flowobjective.FilteringObjective;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.NextObjective;
+import org.onosproject.net.flowobjective.Objective;
+import org.onosproject.net.flowobjective.ObjectiveError;
+import org.slf4j.Logger;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.RemovalCause;
+import com.google.common.cache.RemovalNotification;
+
+/**
+ * Support for FlowObjectives in the EA1000.
+ *
+ * Used with the CarrierEthernet App
+ *
+ */
+public class EA1000Pipeliner extends AbstractHandlerBehaviour implements Pipeliner {
+
+    protected final Logger log = getLogger(getClass());
+    protected ServiceDirectory serviceDirectory;
+    protected FlowRuleService flowRuleService;
+    protected DeviceId deviceId;
+    protected Cache<Integer, NextObjective> pendingNext;
+    protected Integer evcIdBase = 1;
+
+    @Override
+    public void init(DeviceId deviceId, PipelinerContext context) {
+        this.serviceDirectory = context.directory();
+        this.deviceId = deviceId;
+
+        flowRuleService = serviceDirectory.get(FlowRuleService.class);
+
+        pendingNext = CacheBuilder.newBuilder()
+                .expireAfterWrite(20, TimeUnit.SECONDS)
+                .removalListener((RemovalNotification<Integer, NextObjective> notification) -> {
+                    if (notification.getCause() == RemovalCause.EXPIRED) {
+                        notification.getValue().context()
+                                .ifPresent(c -> c.onError(notification.getValue(),
+                                        ObjectiveError.FLOWINSTALLATIONFAILED));
+                    }
+                }).build();
+
+        log.debug("Loaded handler behaviour EA1000Pipeliner for " + handler().data().deviceId().uri());
+    }
+
+    @Override
+    public void filter(FilteringObjective filterObjective) {
+        TrafficTreatment.Builder actions;
+        boolean oppositePort = false;
+        int evcId = -1;
+        switch (filterObjective.type()) {
+            case PERMIT:
+                if (filterObjective.meta() == null) {
+                    actions = DefaultTrafficTreatment.builder().add(Instructions.popVlan());
+                } else {
+                    oppositePort = true; //Experimental - push happens on the opposite port
+                    actions = DefaultTrafficTreatment.builder(filterObjective.meta());
+                    if (filterObjective.meta().metered() != null) {
+                        actions.meter(filterObjective.meta().metered().meterId());
+                    }
+                    actions.transition(0);
+                    boolean isPush = false;
+                    int vid = 0;
+                    for (Instruction inst:filterObjective.meta().immediate()) {
+                        if (inst.type() == Instruction.Type.L2MODIFICATION) {
+                            L2ModificationInstruction l2mod = (L2ModificationInstruction) inst;
+                            if (l2mod.subtype() == L2ModificationInstruction.L2SubType.VLAN_PUSH) {
+                                isPush = true;
+                            } else if (l2mod.subtype() == L2ModificationInstruction.L2SubType.VLAN_ID) {
+                                vid = ((ModVlanIdInstruction) l2mod).vlanId().id();
+                            }
+                        }
+                    }
+                    if (isPush && vid > 0) {
+                        evcId = vid;
+                    }
+                }
+                break;
+            case DENY:
+                actions = (filterObjective.meta() == null) ?
+                        DefaultTrafficTreatment.builder() :
+                        DefaultTrafficTreatment.builder(filterObjective.meta());
+                actions.drop();
+                break;
+            default:
+                log.warn("Unknown filter type: {}", filterObjective.type());
+                actions = DefaultTrafficTreatment.builder().drop();
+        }
+
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+
+        for (Criterion c:filterObjective.conditions()) {
+            if (c.type() == Type.VLAN_VID && evcId == -1) {
+                evcId = ((VlanIdCriterion) c).vlanId().id();
+            }
+            selector.add(c);
+        }
+
+        if (filterObjective.key() != null) {
+            if (oppositePort) {
+                //Experimental
+                Ea1000Port port = Ea1000Port.fromNum(((PortCriterion) filterObjective.key()).port().toLong());
+                selector.matchInPort(PortNumber.portNumber(port.opposite().portNum()));
+            } else {
+                selector.add(filterObjective.key());
+            }
+        }
+
+        FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
+                .forDevice(deviceId)
+                .withSelector(selector.build())
+                .withTreatment(actions.build())
+                .fromApp(filterObjective.appId())
+                .forTable(evcId)
+                .withPriority(filterObjective.priority());
+
+        if (filterObjective.permanent()) {
+            ruleBuilder.makePermanent();
+        } else {
+            ruleBuilder.makeTemporary(filterObjective.timeout());
+        }
+
+        installObjective(ruleBuilder, filterObjective);
+
+        log.debug("filter() of EA1000Pipeliner called for "
+                + handler().data().deviceId().uri()
+                + ". Objective: " + filterObjective);
+    }
+
+    @Override
+    public void forward(ForwardingObjective forwardObjective) {
+        TrafficSelector selector = forwardObjective.selector();
+
+        if (forwardObjective.treatment() != null) {
+            List<Instruction> instructions = forwardObjective.treatment().immediate();
+            if (instructions != null && instructions.size() == 1
+                    && instructions.get(0).type() == Instruction.Type.OUTPUT
+                    && ((OutputInstruction) instructions.get(0)).port() == PortNumber.CONTROLLER) {
+                Set<Criterion> criteria = forwardObjective.selector().criteria();
+                log.info("EA1000 does not yet implement forwarding to CONTROLLER for flow objective for: "
+                        + handler().data().deviceId().uri()
+                        + ". "
+                        + forwardObjective);
+                return;
+            } else {
+                FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
+                        .forDevice(deviceId)
+                        .withSelector(selector)
+                        .fromApp(forwardObjective.appId())
+                        .withPriority(forwardObjective.priority())
+                        .withTreatment(forwardObjective.treatment());
+
+                if (forwardObjective.permanent()) {
+                    ruleBuilder.makePermanent();
+                } else {
+                    ruleBuilder.makeTemporary(forwardObjective.timeout());
+                }
+                installObjective(ruleBuilder, forwardObjective);
+            }
+        } else {
+            NextObjective nextObjective = pendingNext.getIfPresent(forwardObjective.nextId());
+            if (nextObjective != null) {
+                pendingNext.invalidate(forwardObjective.nextId());
+                nextObjective.next().forEach(treat -> {
+                    FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
+                            .forDevice(deviceId)
+                            .withSelector(selector)
+                            .fromApp(forwardObjective.appId())
+                            .withPriority(forwardObjective.priority())
+                            .withTreatment(treat);
+
+                    if (forwardObjective.permanent()) {
+                        ruleBuilder.makePermanent();
+                    } else {
+                        ruleBuilder.makeTemporary(forwardObjective.timeout());
+                    }
+                    installObjective(ruleBuilder, forwardObjective);
+                });
+            } else {
+                forwardObjective.context().ifPresent(c -> c.onError(forwardObjective,
+                        ObjectiveError.GROUPMISSING));
+            }
+        }
+        log.debug("EA1000: Unhandled Forwarding Objective for: "
+                + handler().data().deviceId().uri()
+                + ". "
+                + forwardObjective);
+    }
+
+    @Override
+    public void next(NextObjective nextObjective) {
+        pendingNext.put(nextObjective.id(), nextObjective);
+        nextObjective.context().ifPresent(context -> context.onSuccess(nextObjective));
+
+        log.debug("next() of EA1000Pipeliner called for "
+                + handler().data().deviceId().uri()
+                + ". Objective: " + nextObjective);
+    }
+
+    @Override
+    public List<String> getNextMappings(NextGroup nextGroup) {
+        log.debug("getNextMappings() of EA1000Pipeliner called for "
+                + handler().data().deviceId().uri()
+                + ". Objective: " + nextGroup);
+        return new ArrayList<String>();
+    }
+
+    protected void installObjective(FlowRule.Builder ruleBuilder, Objective objective) {
+        FlowRuleOperations.Builder flowBuilder = FlowRuleOperations.builder();
+        switch (objective.op()) {
+
+            case ADD:
+                flowBuilder.add(ruleBuilder.build());
+                break;
+            case REMOVE:
+                flowBuilder.remove(ruleBuilder.build());
+                break;
+            default:
+                log.warn("Unknown operation {}", objective.op());
+        }
+
+        flowRuleService.apply(flowBuilder.build(new FlowRuleOperationsContext() {
+            @Override
+            public void onSuccess(FlowRuleOperations ops) {
+                objective.context().ifPresent(context -> context.onSuccess(objective));
+            }
+
+            @Override
+            public void onError(FlowRuleOperations ops) {
+                objective.context()
+                        .ifPresent(context -> context.onError(objective, ObjectiveError.FLOWINSTALLATIONFAILED));
+            }
+        }));
+    }
+}
diff --git a/drivers/microsemi/ea1000driver/src/main/java/org/onosproject/drivers/microsemi/Ea1000DeviceDescription.java b/drivers/microsemi/ea1000driver/src/main/java/org/onosproject/drivers/microsemi/Ea1000DeviceDescription.java
new file mode 100644
index 0000000..ec43833
--- /dev/null
+++ b/drivers/microsemi/ea1000driver/src/main/java/org/onosproject/drivers/microsemi/Ea1000DeviceDescription.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.drivers.microsemi;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.time.OffsetDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.onlab.packet.ChassisId;
+import org.onosproject.drivers.microsemi.yang.IetfSystemNetconfService;
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DefaultDeviceDescription;
+import org.onosproject.net.device.DefaultPortDescription;
+import org.onosproject.net.device.DeviceDescription;
+import org.onosproject.net.device.DeviceDescriptionDiscovery;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.device.PortDescription;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.netconf.NetconfController;
+import org.onosproject.netconf.NetconfException;
+import org.onosproject.netconf.NetconfSession;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.system.rev20160505.ietfsystemmicrosemi.system.AugmentedSysSystem;
+import org.onosproject.yang.gen.v1.http.www.microsemi.com.microsemi.edge.assure.msea.system.rev20160505.ietfsystemmicrosemi.systemstate.platform.AugmentedSysPlatform;
+import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.system.rev20140806.IetfSystem;
+import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev20130715.ietfyangtypes.DateAndTime;
+import org.slf4j.Logger;
+
+public class Ea1000DeviceDescription extends AbstractHandlerBehaviour implements DeviceDescriptionDiscovery {
+
+    private String serialNumber = "unavailable";
+    private String swVersion = "unavailable";
+    private String longitudeStr = null;
+    private String latitudeStr = null;
+    private final Logger log = getLogger(getClass());
+
+    public Ea1000DeviceDescription() {
+        log.info("Loaded handler behaviour Ea1000DeviceDescription.");
+    }
+
+    @Override
+    public DeviceDescription discoverDeviceDetails() {
+        log.info("Adding description for EA1000 device");
+
+        NetconfController controller = checkNotNull(handler().get(NetconfController.class));
+        NetconfSession session = controller.getDevicesMap().get(handler().data().deviceId()).getSession();
+        IetfSystemNetconfService ietfSystemService =
+                (IetfSystemNetconfService) checkNotNull(handler().get(IetfSystemNetconfService.class));
+
+        try {
+            IetfSystem system = ietfSystemService.getIetfSystemInit(session);
+            if (system != null && system.systemState() != null) {
+                swVersion = system.systemState().platform().osRelease();
+                AugmentedSysPlatform augmentedSysPlatform =
+                        (AugmentedSysPlatform) system.systemState()
+                        .platform().yangAugmentedInfo(AugmentedSysPlatform.class);
+                serialNumber = augmentedSysPlatform.deviceIdentification().serialNumber();
+                DateAndTime deviceDateAndTime = system.systemState().clock().currentDatetime();
+                OffsetDateTime odt =
+                        OffsetDateTime.parse(deviceDateAndTime.string(), DateTimeFormatter.ISO_OFFSET_DATE_TIME);
+                if (odt.getYear() < OffsetDateTime.now(ZoneId.of("UTC")).getYear()) {
+                    OffsetDateTime nowUtc = OffsetDateTime.now(ZoneId.of("UTC"));
+                    log.warn("Date on device is in the past: {}. Setting it to {}", odt.toString(), nowUtc);
+                    ietfSystemService.setCurrentDatetime(nowUtc, session);
+                }
+            }
+
+            if (system != null && system.system() != null) {
+                AugmentedSysSystem augmentedSystem =
+                        (AugmentedSysSystem) system.system().yangAugmentedInfo(AugmentedSysSystem.class);
+                longitudeStr = augmentedSystem.longitude().toPlainString();
+                latitudeStr = augmentedSystem.latitude().toPlainString();
+            }
+        } catch (NetconfException e) {
+            log.error("Unable to retrieve init data from device: " + handler().data().deviceId().toString()
+                    + " Error: " + e.getMessage());
+            e.printStackTrace();
+        }
+
+        DeviceService deviceService = checkNotNull(handler().get(DeviceService.class));
+        DeviceId deviceId = handler().data().deviceId();
+        Device device = deviceService.getDevice(deviceId);
+        DefaultAnnotations.Builder annotationsBuilder = DefaultAnnotations.builder();
+        if (longitudeStr != null && latitudeStr != null) {
+            annotationsBuilder.set(AnnotationKeys.LONGITUDE, longitudeStr)
+                    .set(AnnotationKeys.LATITUDE, latitudeStr).build();
+        } else {
+            log.warn("Longitude and latitude could not be retrieved from device " + deviceId);
+        }
+
+        return new DefaultDeviceDescription(device.id().uri(), Device.Type.OTHER, "Microsemi", "EA1000", swVersion,
+                serialNumber, new ChassisId(), annotationsBuilder.build());
+    }
+
+    @Override
+    public List<PortDescription> discoverPortDetails() {
+
+        List<PortDescription> ports = new ArrayList<PortDescription>();
+
+        DefaultAnnotations annotationOptics = DefaultAnnotations.builder().set(AnnotationKeys.PORT_NAME, "Optics")
+                .build();
+        PortDescription optics = new DefaultPortDescription(PortNumber.portNumber(0), true, Port.Type.FIBER, 1000,
+                annotationOptics);
+        ports.add(optics);
+
+        DefaultAnnotations annotationHost = DefaultAnnotations.builder().set(AnnotationKeys.PORT_NAME, "Host").build();
+        PortDescription host = new DefaultPortDescription(PortNumber.portNumber(1), true, Port.Type.COPPER, 1000,
+                annotationHost);
+        ports.add(host);
+
+        return ports;
+    }
+}
diff --git a/drivers/microsemi/ea1000driver/src/main/java/org/onosproject/drivers/microsemi/FullMetersAvailable.java b/drivers/microsemi/ea1000driver/src/main/java/org/onosproject/drivers/microsemi/FullMetersAvailable.java
new file mode 100644
index 0000000..724792e
--- /dev/null
+++ b/drivers/microsemi/ea1000driver/src/main/java/org/onosproject/drivers/microsemi/FullMetersAvailable.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.drivers.microsemi;
+
+import org.onosproject.net.behaviour.MeterQuery;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+
+/**
+ * Driver which always responds that all Meters are available for the Device.
+ */
+public class FullMetersAvailable extends AbstractHandlerBehaviour implements MeterQuery {
+
+    private static final long MAX_METER = 0x00000FFF;
+
+    @Override
+    public long getMaxMeters() {
+        return MAX_METER;
+    }
+}
diff --git a/drivers/microsemi/ea1000driver/src/main/java/org/onosproject/drivers/microsemi/MicrosemiDriversLoader.java b/drivers/microsemi/ea1000driver/src/main/java/org/onosproject/drivers/microsemi/MicrosemiDriversLoader.java
new file mode 100644
index 0000000..b7f0e68
--- /dev/null
+++ b/drivers/microsemi/ea1000driver/src/main/java/org/onosproject/drivers/microsemi/MicrosemiDriversLoader.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.drivers.microsemi;
+
+import org.apache.felix.scr.annotations.Component;
+import org.onosproject.net.driver.AbstractDriverLoader;
+
+/**
+ * Loader for Microsemi device drivers.
+ */
+@Component(immediate = true)
+public class MicrosemiDriversLoader extends AbstractDriverLoader {
+
+    public MicrosemiDriversLoader() {
+        super("/microsemi-drivers.xml");
+    }
+}
diff --git a/drivers/microsemi/ea1000driver/src/main/java/org/onosproject/drivers/microsemi/NetconfConfigGetter.java b/drivers/microsemi/ea1000driver/src/main/java/org/onosproject/drivers/microsemi/NetconfConfigGetter.java
new file mode 100644
index 0000000..53151bb
--- /dev/null
+++ b/drivers/microsemi/ea1000driver/src/main/java/org/onosproject/drivers/microsemi/NetconfConfigGetter.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.drivers.microsemi;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.io.IOException;
+
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.behaviour.ConfigGetter;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketService;
+import org.onosproject.netconf.NetconfController;
+import org.slf4j.Logger;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * Used with the onos:device-configuration CLI command.
+ *
+ * This allows the full configuration to be retrieved from the device
+ */
+public class NetconfConfigGetter extends AbstractHandlerBehaviour implements ConfigGetter {
+
+    private final Logger log = getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected PacketService packetService;
+
+    private PacketProcessor testProcessor;
+
+    // FIXME the error string should be universal for all implementations of
+    // ConfigGetter
+    public static final String UNABLE_TO_READ_CONFIG = "config retrieval error";
+
+    @Override
+    public String getConfiguration(String type) {
+        DriverHandler handler = handler();
+        NetconfController controller = handler.get(NetconfController.class);
+
+        DeviceId ofDeviceId = handler.data().deviceId();
+        Preconditions.checkNotNull(controller, "Netconf controller is null");
+        if (type == null ||
+                (!type.equalsIgnoreCase("running")
+                        && !type.equalsIgnoreCase("candidate")
+                        && !type.equalsIgnoreCase("startup"))) {
+            log.error("Configuration type must be either 'running', 'startup' or 'candidate'. '{}' is invalid", type);
+            return UNABLE_TO_READ_CONFIG;
+        }
+        try {
+            return controller.getDevicesMap().get(ofDeviceId).getSession().getConfig(type.replace("cfgType=", ""));
+        } catch (IOException e) {
+            log.error("Configuration could not be retrieved {}", e.getMessage());
+        }
+        return UNABLE_TO_READ_CONFIG;
+    }
+}
diff --git a/drivers/microsemi/ea1000driver/src/main/java/org/onosproject/drivers/microsemi/RpcResultParser.java b/drivers/microsemi/ea1000driver/src/main/java/org/onosproject/drivers/microsemi/RpcResultParser.java
new file mode 100644
index 0000000..7589ea7
--- /dev/null
+++ b/drivers/microsemi/ea1000driver/src/main/java/org/onosproject/drivers/microsemi/RpcResultParser.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.drivers.microsemi;
+
+public final class RpcResultParser {
+
+    private RpcResultParser() {
+        //Not called
+    }
+
+    public static String parseXml(final String deviceDescriptionResponse, final String keyWord) {
+
+        int end = deviceDescriptionResponse.lastIndexOf(keyWord);
+        end = deviceDescriptionResponse.lastIndexOf('<', end);
+        int start = deviceDescriptionResponse.lastIndexOf('>', end);
+        if (start > 1 && end > start) {
+            return deviceDescriptionResponse.substring(start + 1, end);
+        } else {
+            return null;
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/drivers/microsemi/ea1000driver/src/main/java/org/onosproject/drivers/microsemi/package-info.java b/drivers/microsemi/ea1000driver/src/main/java/org/onosproject/drivers/microsemi/package-info.java
new file mode 100644
index 0000000..de883d1
--- /dev/null
+++ b/drivers/microsemi/ea1000driver/src/main/java/org/onosproject/drivers/microsemi/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Package for Microsemi device drivers.
+ */
+package org.onosproject.drivers.microsemi;
\ No newline at end of file
diff --git a/drivers/microsemi/ea1000driver/src/main/resources/microsemi-drivers.xml b/drivers/microsemi/ea1000driver/src/main/resources/microsemi-drivers.xml
new file mode 100644
index 0000000..988ac9a
--- /dev/null
+++ b/drivers/microsemi/ea1000driver/src/main/resources/microsemi-drivers.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<drivers>
+	<driver name="microsemi-netconf" extends="netconf" manufacturer="Microsemi"
+		hwVersion="EA1000">
+		<behaviour api="org.onosproject.net.device.DeviceDescriptionDiscovery"
+			impl="org.onosproject.drivers.microsemi.Ea1000DeviceDescription" />
+		<behaviour api="org.onosproject.net.behaviour.ConfigGetter"
+			impl="org.onosproject.drivers.microsemi.NetconfConfigGetter" />
+		<behaviour api="org.onosproject.net.behaviour.Pipeliner"
+			impl="org.onosproject.drivers.microsemi.EA1000Pipeliner" />
+		<behaviour api="org.onosproject.net.flow.FlowRuleProgrammable"
+			impl="org.onosproject.drivers.microsemi.EA1000FlowRuleProgrammable" />
+		<behaviour api="org.onosproject.net.behaviour.MeterQuery"
+			impl="org.onosproject.drivers.microsemi.FullMetersAvailable" />
+<!--         <behaviour api="org.onosproject.incubator.net.l2monitoring.cfm.CfmMepProgrammable" -->
+<!--             impl="org.onosproject.drivers.microsemi.EA1000CfmMepProgrammable"/> -->
+<!--         <behaviour api="org.onosproject.incubator.net.l2monitoring.soam.SoamDmProgrammable" -->
+<!--             impl="org.onosproject.drivers.microsemi.EA1000SoamDmProgrammable"/> -->
+	</driver>
+</drivers>
diff --git a/drivers/microsemi/ea1000driver/src/test/java/org/onosproject/drivers/microsemi/EA1000FlowRuleProgrammableTest.java b/drivers/microsemi/ea1000driver/src/test/java/org/onosproject/drivers/microsemi/EA1000FlowRuleProgrammableTest.java
new file mode 100644
index 0000000..24aad06
--- /dev/null
+++ b/drivers/microsemi/ea1000driver/src/test/java/org/onosproject/drivers/microsemi/EA1000FlowRuleProgrammableTest.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.drivers.microsemi;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.EthType.EtherType;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.VlanId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultFlowEntry;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowEntry.FlowEntryState;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criteria;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.Criterion.Type;
+import org.onosproject.net.flow.criteria.IPCriterion;
+import org.onosproject.net.flow.criteria.PortCriterion;
+import org.onosproject.net.flow.criteria.VlanIdCriterion;
+import org.onosproject.net.flow.instructions.Instruction;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+public class EA1000FlowRuleProgrammableTest {
+    EA1000FlowRuleProgrammable frProgramable;
+
+    @Before
+    public void setUp() throws Exception {
+        frProgramable = new EA1000FlowRuleProgrammable();
+        frProgramable.setHandler(new MockEa1000DriverHandler());
+        assertNotNull(frProgramable.handler().data().deviceId());
+    }
+
+    @After
+    public void tearDown() throws Exception {
+    }
+
+    @Test
+    public void testGetFlowEntries() {
+        //From MockNetconfSession sample of MseaSaFiltering
+        Collection<FlowEntry> flowEntries = frProgramable.getFlowEntries();
+
+        assertNotNull(flowEntries);
+        //There will be 12 flow entries
+        // 2 for IP Src Address filtering
+        // 2 for EVC 7 - one each port
+        // 8 for EVC 8 - one for host port, 7 on optics port because of ceVlanMap 12:14,20:22,25
+        assertEquals(12, flowEntries.size());
+
+        //Test the first Flow Entry
+        Iterator<FlowEntry> feIter = flowEntries.iterator();
+        while (feIter.hasNext()) {
+            FlowEntry fe = feIter.next();
+            assertTrue(fe.isPermanent());
+            assertEquals(EA1000FlowRuleProgrammable.PRIORITY_DEFAULT, fe.priority());
+
+            Set<Criterion> criteria = fe.selector().criteria();
+            IPCriterion ipCr = null;
+            PortNumber port = null;
+            for (Criterion cr:criteria.toArray(new Criterion[criteria.size()])) {
+                if (cr.type() == Criterion.Type.IPV4_SRC) {
+                    ipCr = (IPCriterion) cr;
+                } else if (cr.type() == Criterion.Type.IN_PORT) {
+                    port = ((PortCriterion) cr).port();
+                } else if (cr.type() == Criterion.Type.VLAN_VID) {
+                    VlanId vid = ((VlanIdCriterion) cr).vlanId();
+                } else {
+                    fail("Unexpected Criterion type: " + cr.type().toString());
+                }
+            }
+            if (ipCr != null && (port == null || port.toLong() != 0L)) {
+                fail("Port number not equal 0 when IP Src Address filter is present");
+            }
+
+            List<Instruction> instructions = fe.treatment().allInstructions();
+
+            if (fe.tableId() == 1) {
+                //Note that in MockNetconf session 10.10.10.10/16 was entered
+                //but it has been corrected to the following by the OF implementation
+                assertEquals("10.10.0.0/16", ipCr.ip().toString());
+                assertEquals(FlowEntryState.ADDED, fe.state());
+            } else if (fe.tableId() == 2) {
+                //Likewise 20.30.40.50 has been truncated because of the 18 bit mask
+                assertEquals("20.30.0.0/18", ipCr.ip().toString());
+                assertEquals(FlowEntryState.ADDED, fe.state());
+            } else if (fe.tableId() == 7 || fe.tableId() == 8) {
+                // 7 and 8 are EVC entries - 2 elements - IN_PORT and VLAN_ID
+                assertEquals(2, fe.selector().criteria().size());
+                //In MockNetconfSession we're rigged it so that the last two chars of the
+                //flow id is the same as the VlanId
+                short vlanId = ((VlanIdCriterion) fe.selector().getCriterion(Type.VLAN_VID)).vlanId().toShort();
+                long flowId = fe.id().id();
+                String flowIdStr = String.valueOf(flowId).substring(String.valueOf(flowId).length() - 2);
+                assertEquals(flowIdStr, String.valueOf(vlanId));
+                if (((PortCriterion) fe.selector().getCriterion(Type.IN_PORT)).port().toLong() == 1L) {
+                    assertEquals(Instruction.Type.L2MODIFICATION, instructions.get(0).type());
+                }
+            } else {
+                fail("Unexpected Flow Entry Rule " + fe.tableId());
+            }
+        }
+    }
+
+    @Test
+    public void testSetFlowEntries() {
+        Criterion matchInPort = Criteria.matchInPort(PortNumber.portNumber(0));
+
+        TrafficTreatment treatmentDrop = DefaultTrafficTreatment.builder().drop().build();
+
+        Collection<FlowRule> frAddedList = new HashSet<FlowRule>();
+
+        FlowRule fr4 = new DefaultFlowRule.Builder()
+            .forDevice(frProgramable.handler().data().deviceId())
+            .forTable(4)
+            .withSelector(DefaultTrafficSelector.builder()
+                    .matchIPSrc(IpPrefix.valueOf("192.168.60.0/22"))
+                    .add(matchInPort).build())
+            .withTreatment(treatmentDrop)
+            .fromApp(new DefaultApplicationId(4, "Filter4"))
+            .makePermanent()
+            .withPriority(EA1000FlowRuleProgrammable.PRIORITY_DEFAULT)
+            .build();
+        frAddedList.add(fr4);
+
+        FlowRule fr5 = new DefaultFlowRule.Builder()
+                .forDevice(frProgramable.handler().data().deviceId())
+                .forTable(5)
+                .withSelector(DefaultTrafficSelector.builder()
+                        .matchIPSrc(IpPrefix.valueOf("192.168.50.0/23"))
+                        .add(matchInPort).build())
+                .withTreatment(treatmentDrop)
+                .withCookie(Long.valueOf("5e0000abaa2772", 16))
+                .makePermanent()
+                .withPriority(EA1000FlowRuleProgrammable.PRIORITY_DEFAULT)
+                .build();
+        frAddedList.add(fr5);
+
+        //Add in some EVCs - especially with complex ceVlanMaps
+        FlowRule frEvc1Vid19P0 = new DefaultFlowRule.Builder()
+                .forDevice(frProgramable.handler().data().deviceId())
+                .forTable(1)
+                .withSelector(DefaultTrafficSelector.builder()
+                        .matchInPort(PortNumber.portNumber(0L))
+                        .matchVlanId(VlanId.vlanId((short) 19))
+                        .build())
+                .withTreatment(DefaultTrafficTreatment.builder()
+                        .popVlan()
+                        .build())
+                .withCookie(Long.valueOf("1e0000abaa0019", 16))
+                .makePermanent()
+                .withPriority(EA1000FlowRuleProgrammable.PRIORITY_DEFAULT)
+                .build();
+        frAddedList.add(frEvc1Vid19P0);
+
+        FlowRule frEvc1Vid20P0 = new DefaultFlowRule.Builder()
+                .forDevice(frProgramable.handler().data().deviceId())
+                .forTable(1)
+                .withSelector(DefaultTrafficSelector.builder()
+                        .matchInPort(PortNumber.portNumber(0L))
+                        .matchVlanId(VlanId.vlanId((short) 20))
+                        .build())
+                .withTreatment(DefaultTrafficTreatment.builder()
+                        .popVlan()
+                        .build())
+                .withCookie(Long.valueOf("1e0000abaa0020", 16))
+                .makePermanent()
+                .withPriority(EA1000FlowRuleProgrammable.PRIORITY_DEFAULT)
+                .build();
+        frAddedList.add(frEvc1Vid20P0);
+
+        FlowRule frEvc1Vid21P1 = new DefaultFlowRule.Builder()
+                .forDevice(frProgramable.handler().data().deviceId())
+                .forTable(1)
+                .withSelector(DefaultTrafficSelector.builder()
+                        .matchInPort(PortNumber.portNumber(1L))
+                        .matchVlanId(VlanId.vlanId((short) 21))
+                        .build())
+                .withTreatment(DefaultTrafficTreatment.builder()
+                        .setVlanId(VlanId.vlanId((short) 250))
+                        .pushVlan(EtherType.QINQ.ethType())
+                        .build())
+                .withCookie(Long.valueOf("1e0000abaa0121", 16))
+                .makePermanent()
+                .withPriority(EA1000FlowRuleProgrammable.PRIORITY_DEFAULT)
+                .build();
+        frAddedList.add(frEvc1Vid21P1);
+
+        FlowRule frEvc1Vid22P1 = new DefaultFlowRule.Builder()
+                .forDevice(frProgramable.handler().data().deviceId())
+                .forTable(1)
+                .withSelector(DefaultTrafficSelector.builder()
+                        .matchInPort(PortNumber.portNumber(1L))
+                        .matchVlanId(VlanId.vlanId((short) 22))
+                        .build())
+                .withTreatment(DefaultTrafficTreatment.builder()
+                        .setVlanId(VlanId.vlanId((short) 250))
+                        .pushVlan(EtherType.QINQ.ethType())
+                        .build())
+                .withCookie(Long.valueOf("1e0000abaa0122", 16))
+                .makePermanent()
+                .withPriority(EA1000FlowRuleProgrammable.PRIORITY_DEFAULT)
+                .build();
+        frAddedList.add(frEvc1Vid22P1);
+
+        Collection<FlowRule> returnedFrList = frProgramable.applyFlowRules(frAddedList);
+
+        assertNotNull(returnedFrList);
+        assertEquals(6, returnedFrList.size());
+
+        //Test the scenario like in FlowRuleManager$InternalFlowRuleProviderService.pushFlowMetricsInternal()
+        Map<FlowEntry, FlowEntry> storedRules = Maps.newHashMap();
+        frAddedList.forEach(f -> storedRules.put(new DefaultFlowEntry(f), new DefaultFlowEntry(f)));
+        List<FlowEntry> feList = Lists.newArrayList();
+        returnedFrList.forEach(f -> feList.add(new DefaultFlowEntry(f)));
+
+        for (FlowEntry rule : feList) {
+            FlowEntry fer = storedRules.remove(rule);
+            assertNotNull(fer);
+            assertTrue(fer.exactMatch(rule));
+        }
+
+        for (FlowRule fr:returnedFrList.toArray(new FlowRule[2])) {
+            if (fr.tableId() == 4) {
+                assertEquals("IPV4_SRC:192.168.60.0/22",
+                        ((IPCriterion) fr.selector().getCriterion(Type.IPV4_SRC)).toString());
+            } else if (fr.tableId() == 5) {
+                assertEquals("IPV4_SRC:192.168.50.0/23",
+                        ((IPCriterion) fr.selector().getCriterion(Type.IPV4_SRC)).toString());
+                assertEquals(Long.valueOf("5e0000abaa2772", 16), fr.id().id());
+            } else if (fr.tableId() == 1) {
+                //TODO add in tests
+            } else {
+                fail("Unexpected flow rule " + fr.tableId() + " in test");
+            }
+        }
+    }
+
+    @Test
+    public void testRemoveFlowEntries() {
+        TrafficSelector.Builder tsBuilder = DefaultTrafficSelector.builder();
+        Criterion matchInPort0 = Criteria.matchInPort(PortNumber.portNumber(0));
+        Criterion matchInPort1 = Criteria.matchInPort(PortNumber.portNumber(1));
+
+        TrafficTreatment.Builder trDropBuilder = DefaultTrafficTreatment.builder();
+        TrafficTreatment treatmentDrop = trDropBuilder.drop().build();
+
+        Collection<FlowRule> frRemoveList = new HashSet<FlowRule>();
+        ApplicationId app = new DefaultApplicationId(1, "org.onosproject.rest");
+
+        Criterion matchIpSrc1 = Criteria.matchIPSrc(IpPrefix.valueOf("10.10.10.10/16"));
+        TrafficSelector selector1 = tsBuilder.add(matchIpSrc1).add(matchInPort0).build();
+        FlowRule.Builder frBuilder1 = new DefaultFlowRule.Builder();
+        frBuilder1.forDevice(frProgramable.handler().data().deviceId())
+                .withSelector(selector1)
+                .withTreatment(treatmentDrop)
+                .forTable(2)
+                .fromApp(app)
+                .makePermanent()
+                .withPriority(EA1000FlowRuleProgrammable.PRIORITY_DEFAULT);
+        frRemoveList.add(frBuilder1.build());
+
+        Criterion matchIpSrc2 = Criteria.matchIPSrc(IpPrefix.valueOf("10.30.10.10/16"));
+        TrafficSelector selector2 = tsBuilder.add(matchIpSrc2).add(matchInPort0).build();
+        FlowRule.Builder frBuilder2 = new DefaultFlowRule.Builder();
+        frBuilder2.forDevice(frProgramable.handler().data().deviceId())
+                .withSelector(selector2)
+                .withTreatment(treatmentDrop)
+                .forTable(3)
+                .fromApp(app)
+                .makePermanent()
+                .withPriority(EA1000FlowRuleProgrammable.PRIORITY_DEFAULT)
+                .build();
+        frRemoveList.add(frBuilder2.build());
+
+
+        TrafficTreatment.Builder trVlanPopBuilder = DefaultTrafficTreatment.builder();
+        TrafficTreatment treatmentVlanPop = trVlanPopBuilder.popVlan().build();
+
+
+        Criterion matchVlan710 = Criteria.matchVlanId(VlanId.vlanId((short) 710));
+        TrafficSelector selector3 = DefaultTrafficSelector.builder().add(matchVlan710).add(matchInPort1).build();
+        FlowRule.Builder frBuilder3 = new DefaultFlowRule.Builder();
+        frBuilder3.forDevice(frProgramable.handler().data().deviceId())
+            .withSelector(selector3)
+            .withTreatment(treatmentVlanPop)
+            .forTable(7)
+            .fromApp(app)
+            .makePermanent()
+            .withPriority(EA1000FlowRuleProgrammable.PRIORITY_DEFAULT)
+            .build();
+        frRemoveList.add(frBuilder3.build());
+
+
+        Criterion matchVlan101 = Criteria.matchVlanId(VlanId.vlanId((short) 101));
+        TrafficSelector selector4 = DefaultTrafficSelector.builder().add(matchVlan101).add(matchInPort1).build();
+        FlowRule.Builder frBuilder4 = new DefaultFlowRule.Builder();
+        frBuilder4.forDevice(frProgramable.handler().data().deviceId())
+            .withSelector(selector4)
+            .withTreatment(treatmentVlanPop)
+            .forTable(1)
+            .fromApp(app)
+            .makePermanent()
+            .withPriority(EA1000FlowRuleProgrammable.PRIORITY_DEFAULT)
+            .build();
+        frRemoveList.add(frBuilder4.build());
+
+        Criterion matchVlan102 = Criteria.matchVlanId(VlanId.vlanId((short) 102));
+        TrafficSelector selector5 = DefaultTrafficSelector.builder().add(matchVlan102).add(matchInPort0).build();
+        FlowRule.Builder frBuilder5 = new DefaultFlowRule.Builder();
+        frBuilder5.forDevice(frProgramable.handler().data().deviceId())
+            .withSelector(selector5)
+            .withTreatment(treatmentVlanPop)
+            .forTable(1)
+            .fromApp(app)
+            .makePermanent()
+            .withPriority(EA1000FlowRuleProgrammable.PRIORITY_DEFAULT)
+            .build();
+        frRemoveList.add(frBuilder5.build());
+
+        Collection<FlowRule> removedFrList = frProgramable.removeFlowRules(frRemoveList);
+        assertNotNull(removedFrList);
+        assertEquals(5, removedFrList.size());
+
+        for (FlowRule frRemoved:removedFrList) {
+            assertNotNull(frRemoved);
+        }
+    }
+}
diff --git a/drivers/microsemi/ea1000driver/src/test/java/org/onosproject/drivers/microsemi/EA1000MeterProviderTest.java b/drivers/microsemi/ea1000driver/src/test/java/org/onosproject/drivers/microsemi/EA1000MeterProviderTest.java
new file mode 100644
index 0000000..b00b321
--- /dev/null
+++ b/drivers/microsemi/ea1000driver/src/test/java/org/onosproject/drivers/microsemi/EA1000MeterProviderTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.drivers.microsemi;
+
+import java.util.HashSet;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.drivers.microsemi.yang.MockMseaUniEvcServiceManager;
+import org.onosproject.drivers.microsemi.yang.MockNetconfSessionEa1000;
+import org.onosproject.drivers.netconf.MockNetconfController;
+import org.onosproject.drivers.netconf.MockNetconfDevice;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.meter.Band;
+import org.onosproject.net.meter.DefaultBand;
+import org.onosproject.net.meter.DefaultMeter;
+import org.onosproject.net.meter.Meter;
+import org.onosproject.net.meter.Meter.Unit;
+import org.onosproject.net.meter.MeterId;
+import org.onosproject.net.meter.MeterOperation;
+import org.onosproject.net.meter.MeterOperation.Type;
+import org.onosproject.netconf.NetconfController;
+
+public class EA1000MeterProviderTest {
+
+    private EA1000MeterProvider meterProvider;
+    private NetconfController controller;
+    private DeviceId mockDeviceId;
+    private MockMseaUniEvcServiceManager mseaUniEvcServiceSvc;
+
+    @Before
+    public void setUp() throws Exception {
+        mockDeviceId = DeviceId.deviceId("netconf:1.2.3.4:830");
+        controller = new MockNetconfController();
+        MockNetconfDevice device = (MockNetconfDevice) controller.connectDevice(mockDeviceId);
+        device.setNcSessionImpl(MockNetconfSessionEa1000.class);
+        mseaUniEvcServiceSvc = new MockMseaUniEvcServiceManager();
+        mseaUniEvcServiceSvc.activate();
+        meterProvider = new TestEA1000MeterProvider(controller, mseaUniEvcServiceSvc);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+    }
+
+    @Test
+    public void testPerformMeterOperationDeviceIdMeterAdd() {
+        DeviceId mockDeviceId = DeviceId.deviceId("netconf:1.2.3.4:830");
+
+        Band cbsBand = DefaultBand.builder()
+                .ofType(Band.Type.REMARK) //Committed - CIR & CBS
+                .withRate(37500L)
+                .burstSize(2000)
+                .dropPrecedence((short) 0)
+                .build();
+
+        Band ebsBand = DefaultBand.builder()
+                .ofType(Band.Type.DROP) //Excess - EIR & EBS
+                .withRate(50000L) //The rate at which we drop - for EA 1000 subtract CIR to get EIR
+                .burstSize(3000) //The burst rate to drop at
+                .build();
+
+        Meter.Builder mBuilder = DefaultMeter.builder()
+                .forDevice(mockDeviceId)
+                .withId(MeterId.meterId(1))
+                .fromApp(new DefaultApplicationId(101, "unit.test"))
+                .burst()
+                .withUnit(Unit.KB_PER_SEC)
+                .withBands(new HashSet<Band>() { { add(cbsBand); add(ebsBand); } });
+
+        MeterOperation meterOp = new MeterOperation(mBuilder.build(), Type.ADD);
+
+        meterProvider.performMeterOperation(mockDeviceId, meterOp);
+        //The NETCONF XML generated by this matches the pattern
+        // sampleXmlRegexEditConfigBwpGroup1
+        // in MockNetconfSession
+    }
+
+    @Test
+    public void testPerformMeterOperationDeviceIdMeterRemove() {
+        DeviceId mockDeviceId = DeviceId.deviceId("netconf:1.2.3.4:830");
+
+        Band cbsBand = DefaultBand.builder()
+                .ofType(Band.Type.REMARK) //Committed - CIR & CBS
+                .withRate(37500L)
+                .burstSize(2000)
+                .dropPrecedence((short) 0)
+                .build();
+
+        Meter.Builder mBuilder = DefaultMeter.builder()
+                .forDevice(mockDeviceId)
+                .withId(MeterId.meterId(1))
+                .fromApp(new DefaultApplicationId(101, "unit.test"))
+                .burst()
+                .withBands(new HashSet<Band>() { { add(cbsBand); } });
+
+        MeterOperation meterOp = new MeterOperation(mBuilder.build(), Type.REMOVE);
+
+        meterProvider.performMeterOperation(mockDeviceId, meterOp);
+        //The NETCONF XML generated by this matches the pattern
+        // sampleXmlRegexEditConfigBwpGroup1
+        // in MockNetconfSession
+    }
+
+}
diff --git a/drivers/microsemi/ea1000driver/src/test/java/org/onosproject/drivers/microsemi/MicrosemiDriversLoaderTest.java b/drivers/microsemi/ea1000driver/src/test/java/org/onosproject/drivers/microsemi/MicrosemiDriversLoaderTest.java
new file mode 100644
index 0000000..a53c6e3
--- /dev/null
+++ b/drivers/microsemi/ea1000driver/src/test/java/org/onosproject/drivers/microsemi/MicrosemiDriversLoaderTest.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.drivers.microsemi;
+
+import org.junit.Before;
+import org.onosproject.net.driver.AbstractDriverLoaderTest;
+
+
+/**
+ * Microsemi drivers loader test.
+ */
+public class MicrosemiDriversLoaderTest extends AbstractDriverLoaderTest {
+
+    @Before
+    public void setUp() {
+        loader = new MicrosemiDriversLoader();
+    }
+}
diff --git a/drivers/microsemi/ea1000driver/src/test/java/org/onosproject/drivers/microsemi/MockEa1000DriverHandler.java b/drivers/microsemi/ea1000driver/src/test/java/org/onosproject/drivers/microsemi/MockEa1000DriverHandler.java
new file mode 100644
index 0000000..78a9d32
--- /dev/null
+++ b/drivers/microsemi/ea1000driver/src/test/java/org/onosproject/drivers/microsemi/MockEa1000DriverHandler.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.drivers.microsemi;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.onosproject.core.CoreService;
+import org.onosproject.drivers.microsemi.yang.MockMseaSaFilteringManager;
+import org.onosproject.drivers.microsemi.yang.MockMseaUniEvcServiceManager;
+import org.onosproject.drivers.microsemi.yang.MockNetconfSessionEa1000;
+import org.onosproject.drivers.microsemi.yang.MseaSaFilteringNetconfService;
+import org.onosproject.drivers.microsemi.yang.MseaUniEvcServiceNetconfService;
+import org.onosproject.drivers.netconf.MockCoreService;
+import org.onosproject.drivers.netconf.MockNetconfController;
+import org.onosproject.drivers.netconf.MockNetconfDevice;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.driver.Behaviour;
+import org.onosproject.net.driver.DefaultDriver;
+import org.onosproject.net.driver.DefaultDriverData;
+import org.onosproject.net.driver.Driver;
+import org.onosproject.net.driver.DriverData;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.net.flow.FlowRuleProgrammable;
+import org.onosproject.netconf.NetconfController;
+import org.onosproject.netconf.NetconfException;
+
+/**
+ * A Mock implementation of the DriverHandler to facilitate unit tests.
+ *
+ * This brings in the implementations of MockMseaSaFilteringManager, MockMseaUniEvcServiceManager,
+ * MockCoreService, MockNetconfDevice and MockNetconfSessionEa1000
+ */
+public class MockEa1000DriverHandler implements DriverHandler {
+
+    private static final String MICROSEMI_DRIVERS = "com.microsemi.drivers";
+
+    private DriverData mockDriverData;
+
+    private NetconfController ncc;
+    private MockMseaSaFilteringManager mseaSaFilteringService;
+    private MockMseaUniEvcServiceManager mseaUniEvcService;
+    private CoreService coreService;
+
+    public MockEa1000DriverHandler() throws NetconfException {
+        Map<Class<? extends Behaviour>, Class<? extends Behaviour>> behaviours =
+                new HashMap<Class<? extends Behaviour>, Class<? extends Behaviour>>();
+        behaviours.put(FlowRuleProgrammable.class, FlowRuleProgrammable.class);
+
+        Map<String, String> properties = new HashMap<String, String>();
+
+        Driver mockDriver =
+                new DefaultDriver("mockDriver", null, "ONOSProject", "1.0.0", "1.0.0", behaviours, properties);
+        DeviceId mockDeviceId = DeviceId.deviceId("netconf:1.2.3.4:830");
+        mockDriverData = new DefaultDriverData(mockDriver, mockDeviceId);
+
+
+        ncc = new MockNetconfController();
+        MockNetconfDevice device = (MockNetconfDevice) ncc.connectDevice(mockDeviceId);
+        device.setNcSessionImpl(MockNetconfSessionEa1000.class);
+
+        mseaSaFilteringService = new MockMseaSaFilteringManager();
+        mseaSaFilteringService.activate();
+
+        mseaUniEvcService = new MockMseaUniEvcServiceManager();
+        mseaUniEvcService.activate();
+
+        coreService = new MockCoreService();
+        coreService.registerApplication(MICROSEMI_DRIVERS);
+    }
+
+    @Override
+    public Driver driver() {
+        return mockDriverData.driver();
+    }
+
+    @Override
+    public DriverData data() {
+        return mockDriverData;
+    }
+
+    @Override
+    public <T extends Behaviour> T behaviour(Class<T> behaviourClass) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public <T> T get(Class<T> serviceClass) {
+        if (serviceClass.equals(NetconfController.class)) {
+            return (T) ncc;
+
+        } else if (serviceClass.equals(MseaSaFilteringNetconfService.class)) {
+            return (T) mseaSaFilteringService;
+
+        } else if (serviceClass.equals(MseaUniEvcServiceNetconfService.class)) {
+            return (T) mseaUniEvcService;
+
+        } else if (serviceClass.equals(CoreService.class)) {
+            return (T) coreService;
+
+        }
+
+        return null;
+    }
+
+}
diff --git a/drivers/microsemi/ea1000driver/src/test/java/org/onosproject/drivers/microsemi/RpcResultParserTest.java b/drivers/microsemi/ea1000driver/src/test/java/org/onosproject/drivers/microsemi/RpcResultParserTest.java
new file mode 100644
index 0000000..64dd80e
--- /dev/null
+++ b/drivers/microsemi/ea1000driver/src/test/java/org/onosproject/drivers/microsemi/RpcResultParserTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.drivers.microsemi;
+
+import static org.junit.Assert.*;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+import org.junit.Test;
+
+public class RpcResultParserTest {
+
+    private static final String SAMPLE1_XML = "/systemReply-Sample1.xml";
+    private static final String SAMPLE2_XML = "/systemReply-Sample2.xml";
+
+    @Test
+    public void testSerialNumber1() {
+        String serialNumberReply = loadXml(SAMPLE1_XML);
+        String serialNumber = RpcResultParser.parseXml(serialNumberReply, "serial-number");
+        assertEquals("Eagle Simulator.", serialNumber);
+    }
+
+    @Test
+    public void testSerialNumber2() {
+        String serialNumberReply = loadXml(SAMPLE2_XML);
+        String serialNumber = RpcResultParser.parseXml(serialNumberReply, "serial-number");
+        assertEquals(null, serialNumber);
+    }
+
+    @Test
+    public void testOsRelease1() {
+        String osReleaseReply = loadXml(SAMPLE1_XML);
+        String osRelease = RpcResultParser.parseXml(osReleaseReply, "os-release");
+        assertEquals("2.6.33-arm1-MSEA1000--00326-g643be76.x.0.0.212", osRelease);
+    }
+
+    @Test
+    public void testOsRelease2() {
+        String osReleaseReply = loadXml(SAMPLE2_XML);
+        String osRelease = RpcResultParser.parseXml(osReleaseReply, "os-release");
+        assertEquals(null, osRelease);
+    }
+
+    @Test
+    public void testLongitude() {
+        String longitudeReply = loadXml(SAMPLE1_XML);
+        String longitudeStr = RpcResultParser.parseXml(longitudeReply, "longitude");
+        assertEquals("-8.4683990", longitudeStr);
+    }
+
+    @Test
+    public void testLatitude() {
+        String latitudeReply = loadXml(SAMPLE1_XML);
+        String latitudeStr = RpcResultParser.parseXml(latitudeReply, "latitude");
+        assertEquals("51.9036140", latitudeStr);
+    }
+
+
+    private static String loadXml(final String fileName) {
+
+        InputStream inputStream = RpcResultParserTest.class.getResourceAsStream(fileName);
+        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
+        StringBuilder result = new StringBuilder();
+        try {
+            String line;
+            while ((line = reader.readLine()) != null) {
+                result.append(line);
+            }
+        } catch (IOException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+        return result.toString();
+    }
+}
\ No newline at end of file
diff --git a/drivers/microsemi/ea1000driver/src/test/java/org/onosproject/drivers/microsemi/TestEA1000MeterProvider.java b/drivers/microsemi/ea1000driver/src/test/java/org/onosproject/drivers/microsemi/TestEA1000MeterProvider.java
new file mode 100644
index 0000000..e56e121
--- /dev/null
+++ b/drivers/microsemi/ea1000driver/src/test/java/org/onosproject/drivers/microsemi/TestEA1000MeterProvider.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.drivers.microsemi;
+
+import org.onosproject.drivers.microsemi.yang.MseaUniEvcServiceNetconfService;
+import org.onosproject.netconf.NetconfController;
+
+public class TestEA1000MeterProvider extends EA1000MeterProvider {
+
+    public TestEA1000MeterProvider(NetconfController controller,
+            MseaUniEvcServiceNetconfService mseaUniEvcServiceSvc) {
+        this.controller = controller;
+        this.mseaUniEvcServiceSvc = mseaUniEvcServiceSvc;
+    }
+}
diff --git a/drivers/microsemi/ea1000driver/src/test/resources/CarrierEthernetFeature-sampleEvcConfig1.txt b/drivers/microsemi/ea1000driver/src/test/resources/CarrierEthernetFeature-sampleEvcConfig1.txt
new file mode 100644
index 0000000..04db8030
--- /dev/null
+++ b/drivers/microsemi/ea1000driver/src/test/resources/CarrierEthernetFeature-sampleEvcConfig1.txt
@@ -0,0 +1,85 @@
+onos> ce-evc-list
+CarrierEthernetVirtualConnection{
+    id=EP-Line-1, cfgId=evpl1, type=POINT_TO_POINT, state=ACTIVE,
+    UNIs=[
+        CarrierEthernetUni{
+            id=netconf:192.168.56.10:830/0,
+            cfgId=netconf:192.168.56.10:830/0,
+            role=Root,
+            refCount=0,
+            ceVlanIds=[100],
+            capacity=1000000000,
+            usedCapacity=3.0E8,
+            bandwidthProfiles=[
+                CarrierEthernetBandwidthProfile{
+                    id=FC-1,
+                    type=EVC,
+                    cir=3.0E8,
+                    cbs=2000,
+                    eir=0.0,
+                    ebs=0}]},
+        CarrierEthernetUni{
+            id=netconf:192.168.56.20:830/0,
+            cfgId=netconf:192.168.56.20:830/0,
+            role=Root,
+            refCount=0,
+            ceVlanIds=[100],
+            capacity=1000000000,
+            usedCapacity=3.0E8,
+            bandwidthProfiles=[
+                CarrierEthernetBandwidthProfile{
+                    id=FC-1,
+                    type=EVC,
+                    cir=3.0E8,
+                    cbs=2000,
+                    eir=0.0,
+                    ebs=0}]}],
+    FCs=[
+        CarrierEthernetForwardingConstruct{
+            id=FC-1,
+            cfgId=null,
+            type=POINT_TO_POINT,
+            vlanId=1,
+            metroConnectId=null,
+            refCount=1,
+            LTPs=[
+                CarrierEthernetLogicalTerminationPoint{
+                    id=netconf:192.168.56.10:830/0,
+                    cfgId=netconf:192.168.56.10:830/0,
+                    role=Root,
+                    ni=CarrierEthernetUni{
+                        id=netconf:192.168.56.10:830/0,
+                        cfgId=netconf:192.168.56.10:830/0,
+                        role=Root,
+                        refCount=0,
+                        ceVlanIds=[100],
+                        capacity=1000000000,
+                        usedCapacity=3.0E8,
+                        bandwidthProfiles=[
+                            CarrierEthernetBandwidthProfile{
+                                id=FC-1,
+                                type=EVC,
+                                cir=3.0E8,
+                                cbs=2000,
+                                eir=0.0,
+                                ebs=0}]}},
+                CarrierEthernetLogicalTerminationPoint{
+                    id=netconf:192.168.56.20:830/0,
+                    cfgId=netconf:192.168.56.20:830/0,
+                    role=Root,
+                    ni=CarrierEthernetUni{
+                        id=netconf:192.168.56.20:830/0,
+                        cfgId=netconf:192.168.56.20:830/0,
+                        role=Root,
+                        refCount=0,
+                        ceVlanIds=[100],
+                        capacity=1000000000,
+                        usedCapacity=3.0E8,
+                        bandwidthProfiles=[
+                            CarrierEthernetBandwidthProfile{
+                                id=FC-1,
+                                type=EVC,
+                                cir=3.0E8,
+                                cbs=2000,
+                                eir=0.0,
+                                ebs=0}]}}]}]}
diff --git a/drivers/microsemi/ea1000driver/src/test/resources/getConfigSaFiltering.xml b/drivers/microsemi/ea1000driver/src/test/resources/getConfigSaFiltering.xml
new file mode 100644
index 0000000..53bfe46
--- /dev/null
+++ b/drivers/microsemi/ea1000driver/src/test/resources/getConfigSaFiltering.xml
@@ -0,0 +1,11 @@
+<rpc message-id="101"
+     xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <get-config>
+        <source>
+            <running/>
+        </source>
+        <filter type="subtree">
+            <source-ipaddress-filtering xmlns="http://www.microsemi.com/microsemi-edge-assure/msea-sa-filtering"/>
+        </filter>
+    </get-config>
+</rpc>
\ No newline at end of file
diff --git a/drivers/microsemi/ea1000driver/src/test/resources/getConfigSaFilteringReply.xml b/drivers/microsemi/ea1000driver/src/test/resources/getConfigSaFilteringReply.xml
new file mode 100644
index 0000000..6e1e040
--- /dev/null
+++ b/drivers/microsemi/ea1000driver/src/test/resources/getConfigSaFilteringReply.xml
@@ -0,0 +1,14 @@
+<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="7">
+    <data>
+        <source-ipaddress-filtering xmlns="http://www.microsemi.com/microsemi-edge-assure/msea-sa-filtering">
+            <interface-eth0>
+                <source-address-range>
+                    <range-id>1</range-id>
+                    <ipv4-address-prefix>10.10.10.10/16</ipv4-address-prefix>
+                    <name>Filter1</name>
+                </source-address-range>
+                <filter-admin-state>inactive</filter-admin-state>
+            </interface-eth0>
+        </source-ipaddress-filtering>
+    </data>
+</rpc-reply>
\ No newline at end of file
diff --git a/drivers/microsemi/ea1000driver/src/test/resources/systemReply-Sample1.xml b/drivers/microsemi/ea1000driver/src/test/resources/systemReply-Sample1.xml
new file mode 100644
index 0000000..206bf45
--- /dev/null
+++ b/drivers/microsemi/ea1000driver/src/test/resources/systemReply-Sample1.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="2">
+    <data>
+        <system xmlns="urn:ietf:params:xml:ns:yang:ietf-system">
+            <longitude xmlns="http://www.microsemi.com/microsemi-edge-assure/msea-system">-8.4683990</longitude>
+            <latitude xmlns="http://www.microsemi.com/microsemi-edge-assure/msea-system">51.9036140</latitude>
+        </system>
+        <system-state xmlns="urn:ietf:params:xml:ns:yang:ietf-system" xmlns:sysms="http://www.microsemi.com/microsemi-edge-assure/msea-system">
+            <platform>
+                <os-release>2.6.33-arm1-MSEA1000--00326-g643be76.x.0.0.212</os-release>
+                <sysms:device-identification>
+                    <sysms:serial-number>Eagle Simulator.</sysms:serial-number>
+                </sysms:device-identification>
+            </platform>
+        </system-state>
+    </data>
+</rpc-reply>
\ No newline at end of file
diff --git a/drivers/microsemi/ea1000driver/src/test/resources/systemReply-Sample2.xml b/drivers/microsemi/ea1000driver/src/test/resources/systemReply-Sample2.xml
new file mode 100644
index 0000000..9592538
--- /dev/null
+++ b/drivers/microsemi/ea1000driver/src/test/resources/systemReply-Sample2.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="2">
+  <data>
+    <system-state xmlns="urn:ietf:params:xml:ns:yang:ietf-system">
+      <platform/>
+    </system-state>
+  </data>
+</rpc-reply>
\ No newline at end of file