[ONOS-7116] Implement MeterProgrammable and MeterDriverProvider

Change-Id: I398edda11a6b77b66d79758cf3afab42976e8ff3
diff --git a/core/api/src/main/java/org/onosproject/net/meter/MeterProgrammable.java b/core/api/src/main/java/org/onosproject/net/meter/MeterProgrammable.java
new file mode 100644
index 0000000..06a303f
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/meter/MeterProgrammable.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.meter;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.net.driver.HandlerBehaviour;
+
+import java.util.Collection;
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * Meter programmable device behaviour.
+ */
+@Beta
+public interface MeterProgrammable extends HandlerBehaviour {
+
+    /**
+     * Performs a meter operation on this device.
+     *
+     * @param meterOp operation to be performed
+     * @return Completable future with result of the operation
+     */
+    CompletableFuture<Boolean> performMeterOperation(MeterOperation meterOp);
+
+    /**
+     * Queries the meters from the device.
+     *
+     * @return completable future with the collection of meters
+     */
+    CompletableFuture<Collection<Meter>> getMeters();
+}
+
+
diff --git a/core/net/src/main/java/org/onosproject/net/meter/impl/MeterDriverProvider.java b/core/net/src/main/java/org/onosproject/net/meter/impl/MeterDriverProvider.java
new file mode 100644
index 0000000..e6151b0
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/meter/impl/MeterDriverProvider.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.meter.impl;
+
+import com.google.common.collect.Sets;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.meter.Meter;
+import org.onosproject.net.meter.MeterOperation;
+import org.onosproject.net.meter.MeterOperations;
+import org.onosproject.net.meter.MeterProgrammable;
+import org.onosproject.net.meter.MeterProvider;
+
+import org.onosproject.net.meter.MeterProviderService;
+import org.onosproject.net.provider.AbstractProvider;
+import org.onosproject.net.provider.ProviderId;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED;
+import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED;
+
+/**
+ * Driver-based Meter provider.
+ */
+public class MeterDriverProvider extends AbstractProvider implements MeterProvider {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    // To be extracted for reuse as we deal with other.
+    private static final String SCHEME = "default";
+    private static final String PROVIDER_NAME = "org.onosproject.provider.meter";
+
+    // potentially positive device event
+    private static final Set<DeviceEvent.Type> POSITIVE_DEVICE_EVENT =
+            Sets.immutableEnumSet(DEVICE_ADDED,
+                    DEVICE_AVAILABILITY_CHANGED);
+
+    protected DeviceService deviceService;
+    protected MastershipService mastershipService;
+    MeterProviderService meterProviderService;
+    int pollFrequency;
+
+    private InternalDeviceListener deviceListener = new InternalDeviceListener();
+    private ScheduledExecutorService executor
+            = newSingleThreadScheduledExecutor(groupedThreads("MeterDriverProvider", "%d", log));
+    private ScheduledFuture<?> poller = null;
+
+    public MeterDriverProvider() {
+        super(new ProviderId(SCHEME, PROVIDER_NAME));
+    }
+
+    /**
+     * Initializes the provider with the necessary device service, meter provider service,
+     * mastership service and poll frequency.
+     *
+     * @param deviceService        device service
+     * @param meterProviderService meter provider service
+     * @param mastershipService    mastership service
+     * @param pollFrequency        meter entry poll frequency
+     */
+    void init(DeviceService deviceService, MeterProviderService meterProviderService,
+              MastershipService mastershipService, int pollFrequency) {
+        this.deviceService = deviceService;
+        this.meterProviderService = meterProviderService;
+        this.mastershipService = mastershipService;
+        this.pollFrequency = pollFrequency;
+
+        deviceService.addListener(deviceListener);
+
+        if (poller != null && !poller.isCancelled()) {
+            poller.cancel(false);
+        }
+
+        poller = executor.scheduleAtFixedRate(this::pollMeters, pollFrequency,
+                pollFrequency, TimeUnit.SECONDS);
+
+    }
+
+    void terminate() {
+        deviceService.removeListener(deviceListener);
+        deviceService = null;
+        meterProviderService = null;
+        mastershipService = null;
+        poller.cancel(true);
+        executor.shutdown();
+    }
+
+    private void pollMeters() {
+        deviceService.getAvailableDevices().forEach(device -> {
+            if (mastershipService.isLocalMaster(device.id()) &&
+                    device.is(MeterProgrammable.class)) {
+                pollDeviceMeters(device.id());
+            }
+        });
+    }
+
+    @Override
+    public void performMeterOperation(DeviceId deviceId, MeterOperations meterOps) {
+        meterOps.operations().forEach(meterOperation -> performMeterOperation(deviceId, meterOperation));
+    }
+
+    @Override
+    public void performMeterOperation(DeviceId deviceId, MeterOperation meterOp) {
+        MeterProgrammable programmable = getMeterProgrammable(deviceId);
+        if (programmable != null) {
+            programmable.performMeterOperation(meterOp);
+        }
+    }
+
+    private void pollDeviceMeters(DeviceId deviceId) {
+        Collection<Meter> meters = null;
+        try {
+            meters = getMeterProgrammable(deviceId).getMeters().get(pollFrequency, TimeUnit.SECONDS);
+        } catch (InterruptedException | ExecutionException | TimeoutException e) {
+            log.warn("Unable to get the Meters from {}, error: {}", deviceId, e.getMessage());
+            log.debug("Exception: ", e);
+        }
+        meterProviderService.pushMeterMetrics(deviceId, meters);
+    }
+
+    private MeterProgrammable getMeterProgrammable(DeviceId deviceId) {
+        Device device = deviceService.getDevice(deviceId);
+        if (device.is(MeterProgrammable.class)) {
+            return device.as(MeterProgrammable.class);
+        } else {
+            log.debug("Device {} is not meter programmable", deviceId);
+            return null;
+        }
+    }
+
+    private class InternalDeviceListener implements DeviceListener {
+
+        @Override
+        public void event(DeviceEvent event) {
+            executor.execute(() -> handleEvent(event));
+        }
+
+        @Override
+        public boolean isRelevant(DeviceEvent event) {
+            Device device = event.subject();
+            return POSITIVE_DEVICE_EVENT.contains(event.type()) &&
+                    device.is(MeterProgrammable.class);
+        }
+
+        private void handleEvent(DeviceEvent event) {
+            Device device = event.subject();
+            boolean isRelevant = mastershipService.isLocalMaster(device.id()) &&
+                    deviceService.isAvailable(device.id());
+
+            if (isRelevant) {
+                pollDeviceMeters(device.id());
+            }
+        }
+    }
+}
diff --git a/core/net/src/main/java/org/onosproject/net/meter/impl/MeterManager.java b/core/net/src/main/java/org/onosproject/net/meter/impl/MeterManager.java
index 0d7ab42..2bd9746 100644
--- a/core/net/src/main/java/org/onosproject/net/meter/impl/MeterManager.java
+++ b/core/net/src/main/java/org/onosproject/net/meter/impl/MeterManager.java
@@ -18,12 +18,17 @@
 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.Modified;
 import org.apache.felix.scr.annotations.Property;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
 import org.onlab.util.TriConsumer;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.mastership.MastershipService;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.DriverService;
 import org.onosproject.net.meter.DefaultMeter;
 import org.onosproject.net.meter.Meter;
 import org.onosproject.net.meter.MeterEvent;
@@ -44,15 +49,19 @@
 import org.onosproject.net.meter.MeterStoreResult;
 import org.onosproject.net.provider.AbstractListenerProviderRegistry;
 import org.onosproject.net.provider.AbstractProviderService;
+import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
 
 import java.util.Collection;
+import java.util.Dictionary;
 import java.util.Map;
 import java.util.concurrent.ExecutorService;
 import java.util.stream.Collectors;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Strings.isNullOrEmpty;
 import static java.util.concurrent.Executors.newFixedThreadPool;
+import static org.onlab.util.Tools.get;
 import static org.onlab.util.Tools.groupedThreads;
 import static org.slf4j.LoggerFactory.getLogger;
 
@@ -81,13 +90,33 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     private MeterStore store;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DriverService driverService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DeviceService deviceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ComponentConfigService cfgService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected MastershipService mastershipService;
+
+    private static final int DEFAULT_POLL_FREQUENCY = 30;
+    @Property(name = "fallbackMeterPollFrequency", intValue = DEFAULT_POLL_FREQUENCY,
+            label = "Frequency (in seconds) for polling meters via fallback provider")
+    private int fallbackMeterPollFrequency = DEFAULT_POLL_FREQUENCY;
+
     private TriConsumer<MeterRequest, MeterStoreResult, Throwable> onComplete;
 
     private ExecutorService executorService;
 
+    private final MeterDriverProvider defaultProvider = new MeterDriverProvider();
+
     @Activate
-    public void activate() {
+    public void activate(ComponentContext context) {
         store.setDelegate(delegate);
+        cfgService.registerProperties(getClass());
         eventDispatcher.addSink(MeterEvent.class, listenerRegistry);
 
         onComplete = (request, result, error) -> {
@@ -107,17 +136,50 @@
 
         executorService = newFixedThreadPool(numThreads,
                 groupedThreads(GROUP_THREAD_NAME, WORKER_PATTERN, log));
+        modified(context);
         log.info("Started");
     }
 
+    @Modified
+    public void modified(ComponentContext context) {
+        if (context != null) {
+            readComponentConfiguration(context);
+        }
+        defaultProvider.init(deviceService, createProviderService(defaultProvider),
+                mastershipService, fallbackMeterPollFrequency);
+    }
+
     @Deactivate
     public void deactivate() {
+        defaultProvider.terminate();
         store.unsetDelegate(delegate);
         eventDispatcher.removeSink(MeterEvent.class);
+        cfgService.unregisterProperties(getClass(), false);
         executorService.shutdown();
         log.info("Stopped");
     }
 
+    /**
+     * Extracts properties from the component configuration context.
+     *
+     * @param context the component context
+     */
+    private void readComponentConfiguration(ComponentContext context) {
+        Dictionary<?, ?> properties = context.getProperties();
+
+        String s = get(properties, "fallbackMeterPollFrequency");
+        try {
+            fallbackMeterPollFrequency = isNullOrEmpty(s) ? DEFAULT_POLL_FREQUENCY : Integer.parseInt(s);
+        } catch (NumberFormatException e) {
+            fallbackMeterPollFrequency = DEFAULT_POLL_FREQUENCY;
+        }
+    }
+
+    @Override
+    protected MeterProvider defaultProvider() {
+        return defaultProvider;
+    }
+
     @Override
     protected MeterProviderService createProviderService(MeterProvider provider) {
         return new InternalMeterProviderService(provider);
diff --git a/core/net/src/test/java/org/onosproject/net/meter/impl/MeterManagerTest.java b/core/net/src/test/java/org/onosproject/net/meter/impl/MeterManagerTest.java
index 51d3384..0ac5d85 100644
--- a/core/net/src/test/java/org/onosproject/net/meter/impl/MeterManagerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/meter/impl/MeterManagerTest.java
@@ -15,12 +15,16 @@
  */
 package org.onosproject.net.meter.impl;
 
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Sets;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.onlab.junit.TestTools;
 import org.onlab.junit.TestUtils;
 import org.onlab.packet.IpAddress;
+import org.onosproject.cfg.ComponentConfigAdapter;
 import org.onosproject.cluster.ClusterServiceAdapter;
 import org.onosproject.cluster.ControllerNode;
 import org.onosproject.cluster.DefaultControllerNode;
@@ -28,7 +32,20 @@
 import org.onosproject.common.event.impl.TestEventDispatcher;
 import org.onosproject.incubator.store.meter.impl.DistributedMeterStore;
 import org.onosproject.mastership.MastershipServiceAdapter;
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DefaultDevice;
+import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.MastershipRole;
+import org.onosproject.net.behaviour.MeterQuery;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.device.DeviceServiceAdapter;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.driver.DefaultDriver;
+import org.onosproject.net.driver.DriverRegistry;
+import org.onosproject.net.driver.impl.DriverManager;
+import org.onosproject.net.driver.impl.DriverRegistryManager;
 import org.onosproject.net.meter.Band;
 import org.onosproject.net.meter.DefaultBand;
 import org.onosproject.net.meter.DefaultMeter;
@@ -39,10 +56,12 @@
 import org.onosproject.net.meter.MeterId;
 import org.onosproject.net.meter.MeterOperation;
 import org.onosproject.net.meter.MeterOperations;
+import org.onosproject.net.meter.MeterProgrammable;
 import org.onosproject.net.meter.MeterProvider;
 import org.onosproject.net.meter.MeterProviderRegistry;
 import org.onosproject.net.meter.MeterProviderService;
 import org.onosproject.net.meter.MeterRequest;
+import org.onosproject.net.meter.MeterService;
 import org.onosproject.net.meter.MeterState;
 import org.onosproject.net.provider.AbstractProvider;
 import org.onosproject.net.provider.ProviderId;
@@ -50,7 +69,11 @@
 
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
 import java.util.Set;
+import java.util.concurrent.CompletableFuture;
 
 import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.*;
@@ -69,9 +92,27 @@
     // Test ip address
     private static final IpAddress LOCALHOST = IpAddress.valueOf("127.0.0.1");
 
-    // Provider id used during the tests
     private static final ProviderId PID = new ProviderId("of", "foo");
 
+    private static final ProviderId PROGRAMMABLE_PROVIDER = new ProviderId("foo", "foo");
+    private static final DeviceId PROGRAMMABLE_DID = DeviceId.deviceId("test:002");
+
+    private static final DefaultAnnotations ANNOTATIONS =
+            DefaultAnnotations.builder().set(AnnotationKeys.DRIVER, "foo").build();
+
+    private static final Device PROGRAMMABLE_DEV =
+            new DefaultDevice(PROGRAMMABLE_PROVIDER, PROGRAMMABLE_DID, Device.Type.SWITCH,
+                    "", "", "", "", null, ANNOTATIONS);
+
+
+    private MeterService service;
+
+    // Test Driver service used during the tests
+    private DriverManager driverService;
+
+    // Test device service used during the tests
+    private DeviceService deviceService;
+
     // Test provider used during the tests
     private TestProvider provider;
 
@@ -95,7 +136,7 @@
     private MeterId mid1 = MeterId.meterId(1);
 
     // Bands used during the tests
-    private Band b1 = DefaultBand.builder()
+    private static Band b1 = DefaultBand.builder()
             .ofType(Band.Type.DROP)
             .withRate(500)
             .build();
@@ -116,6 +157,14 @@
             .withBands(Collections.singletonList(b1))
             .build();
 
+    private static Meter mProgrammable = DefaultMeter.builder()
+            .forDevice(PROGRAMMABLE_DID)
+            .fromApp(APP_ID)
+            .withId(MeterId.meterId(1))
+            .withUnit(Meter.Unit.KB_PER_SEC)
+            .withBands(Collections.singletonList(b1))
+            .build();
+
     // Meter requests used during the tests
     private MeterRequest.Builder m1Request = DefaultMeterRequest.builder()
             .forDevice(did1)
@@ -128,6 +177,12 @@
             .withUnit(Meter.Unit.KB_PER_SEC)
             .withBands(Collections.singletonList(b1));
 
+    private MeterRequest.Builder mProgrammableRequest = DefaultMeterRequest.builder()
+            .forDevice(PROGRAMMABLE_DID)
+            .fromApp(APP_ID)
+            .withUnit(Meter.Unit.KB_PER_SEC)
+            .withBands(Collections.singletonList(b1));
+
     // Meter features used during the tests
     private MeterFeatures mef1 = DefaultMeterFeatures.builder().forDevice(did1)
             .withMaxMeters(3L)
@@ -148,14 +203,27 @@
             .withMaxColors((byte) 0)
             .build();
 
+
     @Before
     public void setup() {
+        //Init step for the deviceService
+        deviceService = new TestDeviceService();
+        //Init step for the driver registry and driver service.
+        DriverRegistryManager driverRegistry = new DriverRegistryManager();
+        driverService = new TestDriverManager(driverRegistry, deviceService);
+        driverRegistry.addDriver(new DefaultDriver("foo", ImmutableList.of(), "",
+                "", "",
+                ImmutableMap.of(MeterProgrammable.class,
+                        TestMeterProgrammable.class, MeterQuery.class, TestMeterQuery.class),
+                ImmutableMap.of()));
+
         // Init step for the store
         meterStore = new DistributedMeterStore();
         // Let's initialize some internal services of the store
         TestUtils.setField(meterStore, "storageService", new TestStorageService());
         TestUtils.setField(meterStore, "clusterService", new TestClusterService());
         TestUtils.setField(meterStore, "mastershipService", new TestMastershipService());
+        TestUtils.setField(meterStore, "driverService", driverService);
         // Activate the store
         meterStore.activate();
         // Init step for the manager
@@ -163,11 +231,19 @@
         // Let's initialize some internal services of the manager
         TestUtils.setField(manager, "store", meterStore);
         injectEventDispatcher(manager, new TestEventDispatcher());
+        manager.deviceService = deviceService;
+        manager.mastershipService = new TestMastershipService();
+        manager.cfgService = new ComponentConfigAdapter();
+        TestUtils.setField(manager, "storageService", new TestStorageService());
         // Init the reference of the registry
         registry = manager;
+
+        manager.driverService = driverService;
+
         // Activate the manager
-        manager.activate();
+        manager.activate(null);
         // Initialize the test provider
+
         provider = new TestProvider(PID);
         // Register the provider against the manager
         providerService = registry.register(provider);
@@ -337,6 +413,61 @@
         assertTrue("The meter was not removed", manager.getMeters(did2).size() == 0);
     }
 
+    @Test
+    public void testAddFromMeterProgrammable()  {
+
+        // Init store
+        initMeterStore();
+
+        manager.submit(mProgrammableRequest.add());
+
+        TestTools.assertAfter(500, () -> {
+
+            assertTrue("The meter was not added", manager.getAllMeters().size() == 1);
+
+            assertThat(manager.getMeter(PROGRAMMABLE_DID, MeterId.meterId(1)), is(mProgrammable));
+        });
+
+    }
+
+    @Test
+    public void testAddBatchFromMeterProgrammable()  {
+
+        // Init store
+        initMeterStore();
+
+        List<MeterOperation> operations = ImmutableList.of(new MeterOperation(mProgrammable, MeterOperation.Type.ADD));
+        manager.defaultProvider().performMeterOperation(PROGRAMMABLE_DID, new MeterOperations(operations));
+
+        TestTools.assertAfter(500, () -> {
+
+            assertTrue("The meter was not added", meterOperations.size() == 1);
+
+            assertTrue("Wrong Meter Operation", meterOperations.get(0).meter().id().equals(mProgrammable.id()));
+        });
+
+    }
+
+    @Test
+    public void testGetFromMeterProgrammable()  {
+
+        // Init store
+        initMeterStore();
+
+        MeterDriverProvider fallback = (MeterDriverProvider) manager.defaultProvider();
+
+        testAddFromMeterProgrammable();
+
+        fallback.init(manager.deviceService, fallback.meterProviderService, manager.mastershipService, 1);
+
+        TestTools.assertAfter(2000, () -> {
+            assertTrue("The meter was not added", manager.getAllMeters().size() == 1);
+            Meter m = manager.getMeters(PROGRAMMABLE_DID).iterator().next();
+            assertEquals("incorrect state", MeterState.ADDED, m.state());
+        });
+
+    }
+
     // Test cluster service
     private final class TestClusterService extends ClusterServiceAdapter {
 
@@ -354,6 +485,65 @@
 
     }
 
+    private static class TestDeviceService extends DeviceServiceAdapter {
+        @Override
+        public int getDeviceCount() {
+            return 1;
+        }
+
+        @Override
+        public Iterable<Device> getDevices() {
+            return ImmutableList.of(PROGRAMMABLE_DEV);
+        }
+
+        @Override
+        public Iterable<Device> getAvailableDevices() {
+            return getDevices();
+        }
+
+        @Override
+        public Device getDevice(DeviceId deviceId) {
+            return PROGRAMMABLE_DEV;
+        }
+    }
+
+    private class TestDriverManager extends DriverManager {
+        TestDriverManager(DriverRegistry registry, DeviceService deviceService) {
+            this.registry = registry;
+            this.deviceService = deviceService;
+            activate();
+        }
+    }
+
+    public static class TestMeterQuery extends AbstractHandlerBehaviour
+            implements MeterQuery {
+        private static final long MAX_METER = 0x00000FFF;
+
+        @Override
+        public long getMaxMeters() {
+            return MAX_METER;
+        }
+    }
+
+    private static List<MeterOperation> meterOperations = new ArrayList<>();
+
+    public static class TestMeterProgrammable extends AbstractHandlerBehaviour
+            implements MeterProgrammable {
+
+        @Override
+        public CompletableFuture<Boolean> performMeterOperation(MeterOperation meterOp) {
+            return CompletableFuture.completedFuture(meterOperations.add(meterOp));
+        }
+
+        @Override
+        public CompletableFuture<Collection<Meter>> getMeters() {
+            //ADD METER
+            DefaultMeter mProgrammableAdded = (DefaultMeter) mProgrammable;
+            mProgrammableAdded.setState(MeterState.ADDED);
+            return CompletableFuture.completedFuture(ImmutableList.of(mProgrammableAdded));
+        }
+    }
+
     private class TestProvider extends AbstractProvider implements MeterProvider {
 
         protected TestProvider(ProviderId id) {
@@ -377,6 +567,11 @@
         public NodeId getMasterFor(DeviceId deviceId) {
             return NID_LOCAL;
         }
+
+        @Override
+        public MastershipRole getLocalRole(DeviceId deviceId) {
+            return MastershipRole.MASTER;
+        }
     }
 
 }
diff --git a/incubator/net/pom.xml b/incubator/net/pom.xml
index 96cd401..34b95f8 100644
--- a/incubator/net/pom.xml
+++ b/incubator/net/pom.xml
@@ -112,6 +112,7 @@
             <artifactId>onos-core-serializers</artifactId>
             <version>${project.version}</version>
         </dependency>
+
     </dependencies>
 
     <build>