Implements policer-meter-mapping [ONOS-5867]

Changes
- Adds Policer config behavior and its implementation
- Implements id mapping
- Adds tests for OpenFlowPolicerConfig
- Adds free id api to MeterService and MeterStore
- Improves test for MeterStore

Change-Id: Ibb47375430e253131d9d9c8a60e61023ee6c8225
diff --git a/core/api/src/main/java/org/onosproject/net/behaviour/trafficcontrol/PolicerConfigurable.java b/core/api/src/main/java/org/onosproject/net/behaviour/trafficcontrol/PolicerConfigurable.java
new file mode 100644
index 0000000..189bf60
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/behaviour/trafficcontrol/PolicerConfigurable.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.behaviour.trafficcontrol;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.net.driver.HandlerBehaviour;
+
+import java.util.Collection;
+
+/**
+ * Behaviour for handling various drivers for policer configurations.
+ */
+@Beta
+public interface PolicerConfigurable extends HandlerBehaviour {
+
+    /**
+     * Allocates a new policer id. There may not be any correspondence with the identifiers
+     * of the technology implementing the Policer in the device. Mapping (if necessary) is left
+     * to the specific implementation.
+     *
+     * @return the policer id or {@link PolicerId#NONE} if there is a failure
+     */
+    PolicerId allocatePolicerId();
+
+    /**
+     * Free a policer id. There may not be any correspondence with the identifiers
+     * of the technology implementing the Policer in the device. Mapping (if necessary) is left
+     * to the specific implementation.
+     *
+     * @param id the policer id
+     */
+    void freePolicerId(PolicerId id);
+
+    // Still WIP, throws NotImplementedException
+    void addPolicer(Policer policer);
+
+    // Still WIP, throws NotImplementedException
+    void deletePolicer(PolicerId id);
+
+    // Still WIP, throws NotImplementedException
+    Policer getPolicer(PolicerId policerId);
+
+    // Still WIP, throws NotImplementedException
+    Collection<Policer> getPolicers();
+
+}
diff --git a/core/api/src/main/java/org/onosproject/net/meter/MeterService.java b/core/api/src/main/java/org/onosproject/net/meter/MeterService.java
index c0a8671..560d94e 100644
--- a/core/api/src/main/java/org/onosproject/net/meter/MeterService.java
+++ b/core/api/src/main/java/org/onosproject/net/meter/MeterService.java
@@ -67,4 +67,21 @@
      * @return a collection of meters
      */
     Collection<Meter> getMeters(DeviceId deviceId);
+
+    /**
+     * Allocates a new meter id in the system.
+     *
+     * @param deviceId the device id
+     * @return the allocated meter id, null if there is an internal error
+     * or there are no meter ids available
+     */
+    MeterId allocateMeterId(DeviceId deviceId);
+
+    /**
+     * Frees the given meter id.
+     *
+     * @param deviceId the device id
+     * @param meterId the id to be freed
+     */
+    void freeMeterId(DeviceId deviceId, MeterId meterId);
 }
diff --git a/core/api/src/test/java/org/onosproject/net/meter/MeterServiceAdapter.java b/core/api/src/test/java/org/onosproject/net/meter/MeterServiceAdapter.java
new file mode 100644
index 0000000..26def99
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/net/meter/MeterServiceAdapter.java
@@ -0,0 +1,71 @@
+/*
+ * 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 org.onosproject.net.DeviceId;
+
+import java.util.Collection;
+
+/**
+ * Testing adapter for the meter service interface.
+ */
+public class MeterServiceAdapter implements MeterService {
+    @Override
+    public Meter submit(MeterRequest meter) {
+        return null;
+    }
+
+    @Override
+    public void withdraw(MeterRequest meter, MeterId meterId) {
+
+    }
+
+    @Override
+    public Meter getMeter(DeviceId deviceId, MeterId id) {
+        return null;
+    }
+
+    @Override
+    public Collection<Meter> getAllMeters() {
+        return null;
+    }
+
+    @Override
+    public Collection<Meter> getMeters(DeviceId deviceId) {
+        return null;
+    }
+
+    @Override
+    public MeterId allocateMeterId(DeviceId deviceId) {
+        return null;
+    }
+
+    @Override
+    public void freeMeterId(DeviceId deviceId, MeterId meterId) {
+
+    }
+
+    @Override
+    public void addListener(MeterListener listener) {
+
+    }
+
+    @Override
+    public void removeListener(MeterListener listener) {
+
+    }
+}
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 2bd9746..5d7965b 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
@@ -243,11 +243,18 @@
         return store.getAllMeters();
     }
 
-    private MeterId allocateMeterId(DeviceId deviceId) {
+    @Override
+    public MeterId allocateMeterId(DeviceId deviceId) {
         // We delegate direclty to the store
         return store.allocateMeterId(deviceId);
     }
 
+    @Override
+    public void freeMeterId(DeviceId deviceId, MeterId meterId) {
+        // We delegate direclty to the store
+        store.freeMeterId(deviceId, meterId);
+    }
+
     private class InternalMeterProviderService
             extends AbstractProviderService<MeterProvider>
             implements MeterProviderService {
diff --git a/drivers/default/src/main/java/org/onosproject/driver/trafficcontrol/OpenFlowPolicerConfigurable.java b/drivers/default/src/main/java/org/onosproject/driver/trafficcontrol/OpenFlowPolicerConfigurable.java
new file mode 100644
index 0000000..1b8c199
--- /dev/null
+++ b/drivers/default/src/main/java/org/onosproject/driver/trafficcontrol/OpenFlowPolicerConfigurable.java
@@ -0,0 +1,140 @@
+/*
+ * 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.driver.trafficcontrol;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.behaviour.trafficcontrol.Policer;
+import org.onosproject.net.behaviour.trafficcontrol.PolicerConfigurable;
+import org.onosproject.net.behaviour.trafficcontrol.PolicerId;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.net.meter.MeterId;
+import org.onosproject.net.meter.MeterService;
+import org.slf4j.Logger;
+
+import java.net.URI;
+import java.util.Collection;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of policer config which allows to add, delete and get policers.
+ */
+public class OpenFlowPolicerConfigurable extends AbstractHandlerBehaviour implements PolicerConfigurable {
+
+    // log
+    private final Logger log = getLogger(OpenFlowPolicerConfigurable.class);
+    // OpenFlow scheme
+    private static final String OF_SCHEME = "of";
+    // Hex
+    private static final int HEX = 16;
+
+    // Create a policer id from a meter id
+    private PolicerId getPolicerIdFromMeterId(MeterId meterId) {
+        // Create URI representing the meter id
+        URI uri = URI.create(OF_SCHEME + ":" + Long.toHexString(meterId.id()));
+        // Return the new policer id
+        return PolicerId.policerId(uri);
+    }
+
+    // Create a meter id from a policer id
+    private MeterId getMeterIdFromPolicerId(PolicerId policerId) {
+        // Get scheme specific part
+        Long id = Long.parseLong(policerId.uri().getSchemeSpecificPart(), HEX);
+        // Return the meter id
+        return MeterId.meterId(id);
+    }
+
+    @Override
+    public PolicerId allocatePolicerId() {
+        // Init step
+        DriverHandler handler = handler();
+        // First step is to get MeterService
+        MeterService meterService = handler.get(MeterService.class);
+        // There was a problem, return none
+        if (meterService == null) {
+            log.warn("MeterService is null");
+            return PolicerId.NONE;
+        }
+        // Let's get the device id
+        DeviceId deviceId = handler.data().deviceId();
+        // Double check correspondence between schemas
+        if (!deviceId.uri().getScheme().equals(OF_SCHEME)) {
+            log.warn("The device {} does not seem to be managed by OpenFlow", deviceId);
+            return PolicerId.NONE;
+        }
+        // Get a new meter id
+        MeterId meterId = meterService.allocateMeterId(deviceId);
+        // There was a problem
+        if (meterId == null) {
+            log.warn("MeterService does not provide valid ids");
+            return PolicerId.NONE;
+        }
+        // Create a policer id from the meter id
+        return getPolicerIdFromMeterId(meterId);
+    }
+
+    @Override
+    public void freePolicerId(PolicerId id) {
+        // Init step
+        DriverHandler handler = handler();
+        // First step is to get MeterService
+        MeterService meterService = handler.get(MeterService.class);
+        // There was a problem, return none
+        if (meterService == null) {
+            log.warn("MeterService is null");
+            return;
+        }
+        // Let's get the device id
+        DeviceId deviceId = handler.data().deviceId();
+        // Double check correspondence with device schema
+        if (!deviceId.uri().getScheme().equals(OF_SCHEME)) {
+            log.warn("The device {} does not seem to be managed by OpenFlow", deviceId);
+            return;
+        }
+        // Double check correspondence with pid schema
+        if (!id.uri().getScheme().equals(OF_SCHEME)) {
+            log.warn("The id {} does not seem to be OpenFlow", id);
+            return;
+        }
+        // Get the meter id
+        MeterId meterId = getMeterIdFromPolicerId(id);
+        // Free the meter id
+        meterService.freeMeterId(deviceId, meterId);
+    }
+
+    @Override
+    public void addPolicer(Policer policer) {
+        throw new UnsupportedOperationException("addPolicer not yet implemented");
+    }
+
+    @Override
+    public void deletePolicer(PolicerId id) {
+        throw new UnsupportedOperationException("deletePolicer not yet implemented");
+    }
+
+    @Override
+    public Policer getPolicer(PolicerId policerId) {
+        throw new UnsupportedOperationException("getPolicer not yet implemented");
+    }
+
+    @Override
+    public Collection<Policer> getPolicers() {
+        throw new UnsupportedOperationException("getPolicers not yet implemented");
+    }
+
+}
diff --git a/drivers/default/src/main/java/org/onosproject/driver/trafficcontrol/package-info.java b/drivers/default/src/main/java/org/onosproject/driver/trafficcontrol/package-info.java
new file mode 100644
index 0000000..165442a
--- /dev/null
+++ b/drivers/default/src/main/java/org/onosproject/driver/trafficcontrol/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * Implementations of the traffic control behaviours.
+ */
+package org.onosproject.driver.trafficcontrol;
diff --git a/drivers/default/src/test/java/org/onosproject/driver/trafficcontrol/OpenFlowPolicerConfigurableTest.java b/drivers/default/src/test/java/org/onosproject/driver/trafficcontrol/OpenFlowPolicerConfigurableTest.java
new file mode 100644
index 0000000..b61ec6a
--- /dev/null
+++ b/drivers/default/src/test/java/org/onosproject/driver/trafficcontrol/OpenFlowPolicerConfigurableTest.java
@@ -0,0 +1,269 @@
+/*
+ * 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.driver.trafficcontrol;
+
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.behaviour.trafficcontrol.PolicerConfigurable;
+import org.onosproject.net.behaviour.trafficcontrol.PolicerId;
+import org.onosproject.net.driver.Behaviour;
+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.driver.DriverService;
+import org.onosproject.net.driver.DriverServiceAdapter;
+import org.onosproject.net.meter.MeterId;
+import org.onosproject.net.meter.MeterServiceAdapter;
+
+import java.util.Comparator;
+import java.util.Set;
+import java.util.TreeSet;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+import static org.onosproject.net.NetTestTools.did;
+
+/**
+ * OpenFlow Policer config test.
+ */
+public class OpenFlowPolicerConfigurableTest {
+
+    // Device id used during the tests
+    private DeviceId ofDid = did("1");
+    private DeviceId fooDid = DeviceId.deviceId(FOO_SCHEME + ":1");
+
+    // Schemes used during the tests
+    private static final String OF_SCHEME = "of";
+    private static final String FOO_SCHEME = "foo";
+
+    // Policer id used during the tests
+    private PolicerId fooPid = PolicerId.policerId(FOO_SCHEME + ":1");
+
+    // Meter ids used during the tests
+    private MeterId mid1 = MeterId.meterId(1);
+    private MeterId mid10 = MeterId.meterId(10);
+    private MeterId mid100 = MeterId.meterId(100);
+
+    // Test Driver service used during the tests
+    private DriverService driverService = new TestDriverService();
+
+    // Test Meter service used during the tests
+    private TestMeterService meterService = new TestMeterService();
+
+    /**
+     * Test allocate policer id.
+     */
+    @Test
+    public void testAllocateId() {
+        // Get device handler
+        DriverHandler driverHandler = driverService.createHandler(ofDid);
+        // Get policer config behavior
+        PolicerConfigurable policerConfigurable = driverHandler.behaviour(PolicerConfigurable.class);
+        // Get policer id
+        PolicerId policerId = policerConfigurable.allocatePolicerId();
+        // Assert that scheme is equal to OF
+        assertThat(policerId.uri().getScheme(), is(OF_SCHEME));
+        // Convert in hex the id
+        String hexId = Long.toHexString((mid1.id()));
+        // Assert that specific part contains hex of meter id
+        assertThat(policerId.uri().getSchemeSpecificPart(), is(hexId));
+    }
+
+    /**
+     * Test no corresponding device.
+     */
+    @Test
+    public void testWrongDevice() {
+        // Get device handler
+        DriverHandler driverHandler = driverService.createHandler(fooDid);
+        // Get policer config behavior
+        PolicerConfigurable policerConfigurable = driverHandler.behaviour(PolicerConfigurable.class);
+        // Get policer id
+        PolicerId policerId = policerConfigurable.allocatePolicerId();
+        // Assert that is none
+        assertThat(policerId, is(PolicerId.NONE));
+    }
+
+    /**
+     * Test meter problems.
+     */
+    @Test
+    public void testMeterNull() {
+        // Get device handler
+        DriverHandler driverHandler = driverService.createHandler(ofDid);
+        // Get policer config behavior
+        PolicerConfigurable policerConfigurable = driverHandler.behaviour(PolicerConfigurable.class);
+        // Get policer id
+        PolicerId policerId = policerConfigurable.allocatePolicerId();
+        // this works
+        assertThat(policerId.uri().getScheme(), is(OF_SCHEME));
+        String hexId = Long.toHexString((mid1.id()));
+        assertThat(policerId.uri().getSchemeSpecificPart(), is(hexId));
+        // Get another policer id
+        policerId = policerConfigurable.allocatePolicerId();
+        assertThat(policerId.uri().getScheme(), is(OF_SCHEME));
+        hexId = Long.toHexString((mid10.id()));
+        assertThat(policerId.uri().getSchemeSpecificPart(), is(hexId));
+        // Get the last policer id
+        policerId = policerConfigurable.allocatePolicerId();
+        assertThat(policerId.uri().getScheme(), is(OF_SCHEME));
+        hexId = Long.toHexString((mid100.id()));
+        assertThat(policerId.uri().getSchemeSpecificPart(), is(hexId));
+        // this does not work
+        policerId = policerConfigurable.allocatePolicerId();
+        // Assert that is none
+        assertThat(policerId, is(PolicerId.NONE));
+    }
+
+    /**
+     * Test free policer id.
+     */
+    @Test
+    public void testFreeId() {
+        // Get device handler
+        DriverHandler driverHandler = driverService.createHandler(ofDid);
+        // Get policer config behavior
+        PolicerConfigurable policerConfigurable = driverHandler.behaviour(PolicerConfigurable.class);
+        // Get policer id
+        PolicerId policerId = policerConfigurable.allocatePolicerId();
+        // this works
+        assertThat(policerId.uri().getScheme(), is(OF_SCHEME));
+        String hexId = Long.toHexString((mid1.id()));
+        assertThat(policerId.uri().getSchemeSpecificPart(), is(hexId));
+        // Verify the allocation before free
+        assertThat(meterService.availableIds.size(), is(2));
+        // Let's free the policer id
+        policerConfigurable.freePolicerId(policerId);
+        // Verify the availability after free
+        assertThat(meterService.availableIds.size(), is(3));
+    }
+
+    /**
+     * Test wrong policer id.
+     */
+    @Test
+    public void testWrongId() {
+        // Get device handler
+        DriverHandler driverHandler = driverService.createHandler(ofDid);
+        // Get policer config behavior
+        PolicerConfigurable policerConfigurable = driverHandler.behaviour(PolicerConfigurable.class);
+        // Get policer id
+        PolicerId policerId = policerConfigurable.allocatePolicerId();
+        // this works
+        assertThat(policerId.uri().getScheme(), is(OF_SCHEME));
+        String hexId = Long.toHexString((mid1.id()));
+        assertThat(policerId.uri().getSchemeSpecificPart(), is(hexId));
+        // Verify the allocation before free
+        assertThat(meterService.availableIds.size(), is(2));
+        // Update the pid with a wrong id (same id but wrong schema)
+        policerId = fooPid;
+        // Let's free the policer id
+        policerConfigurable.freePolicerId(policerId);
+        // Free does not end correctly
+        assertThat(meterService.availableIds.size(), is(2));
+    }
+
+    // Test class for driver handler
+    private class TestDriverHandler implements DriverHandler {
+
+        private final DeviceId deviceId;
+
+        TestDriverHandler(DeviceId deviceId) {
+            this.deviceId = deviceId;
+        }
+
+        @Override
+        public Driver driver() {
+            return null;
+        }
+
+        @Override
+        public DriverData data() {
+            // Just create a fake driver data
+            return new DefaultDriverData(null, deviceId);
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public <T extends Behaviour> T behaviour(Class<T> behaviourClass) {
+            // Let's create the behavior
+            PolicerConfigurable policerConfigurable = new OpenFlowPolicerConfigurable();
+            // Set the handler
+            policerConfigurable.setHandler(this);
+            // Done, return the behavior
+            return (T) policerConfigurable;
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public <T> T get(Class<T> serviceClass) {
+            return (T) meterService;
+        }
+
+        @Override
+        public boolean hasBehaviour(Class<? extends Behaviour> behaviourClass) {
+            return true;
+        }
+    }
+
+    // Test class for driver service
+    private class TestDriverService extends DriverServiceAdapter {
+
+        @Override
+        public DriverHandler createHandler(DeviceId deviceId, String... credentials) {
+            return new TestDriverHandler(deviceId);
+        }
+
+    }
+
+    // Test class for meter service
+    private class TestMeterService extends MeterServiceAdapter {
+
+        // Let's simulate a store
+        Set<MeterId> availableIds = new TreeSet<>(
+                (Comparator<MeterId>) (id1, id2) -> id1.id().compareTo(id2.id())
+        );
+
+        TestMeterService() {
+            availableIds.addAll(ImmutableList.of(mid1, mid10, mid100));
+        }
+
+        @Override
+        public MeterId allocateMeterId(DeviceId deviceId) {
+            // If there are no more ids, return null
+            if (availableIds.isEmpty()) {
+                return null;
+            }
+            // Get the next id
+            MeterId meterId = availableIds.iterator().next();
+            // Make it unavailable
+            availableIds.remove(meterId);
+            // Done, return it
+            return meterId;
+        }
+
+        @Override
+        public void freeMeterId(DeviceId deviceId, MeterId meterId) {
+            // Make the id available
+            availableIds.add(meterId);
+        }
+
+    }
+
+}
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkMeterManager.java b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkMeterManager.java
index d661719..faa6ca7 100644
--- a/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkMeterManager.java
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkMeterManager.java
@@ -183,7 +183,7 @@
                 .getAtomicCounter(String.format(METERCOUNTERIDENTIFIER, deviceId));
     }
 
-    private MeterId allocateMeterId(DeviceId deviceId) {
+    public MeterId allocateMeterId(DeviceId deviceId) {
         long maxMeters = store.getMaxMeters(networkId(), MeterFeaturesKey.key(deviceId));
         if (maxMeters == 0L) {
             // MeterFeatures couldn't be retrieved, trying with queryMeters
@@ -211,6 +211,11 @@
         return MeterId.meterId(id);
     }
 
+    @Override
+    public void freeMeterId(DeviceId deviceId, MeterId meterId) {
+        // Do nothing
+    }
+
     private class InternalMeterProviderService
             extends AbstractVirtualProviderService<VirtualMeterProvider>
             implements VirtualMeterProviderService {
diff --git a/incubator/protobuf/services/nb/src/test/java/org/onosproject/incubator/protobuf/services/nb/GrpcNbMeterServiceTest.java b/incubator/protobuf/services/nb/src/test/java/org/onosproject/incubator/protobuf/services/nb/GrpcNbMeterServiceTest.java
index b309479..46ade10 100644
--- a/incubator/protobuf/services/nb/src/test/java/org/onosproject/incubator/protobuf/services/nb/GrpcNbMeterServiceTest.java
+++ b/incubator/protobuf/services/nb/src/test/java/org/onosproject/incubator/protobuf/services/nb/GrpcNbMeterServiceTest.java
@@ -335,6 +335,16 @@
             }
             return meters;
         }
+
+        @Override
+        public MeterId allocateMeterId(DeviceId deviceId) {
+            return null;
+        }
+
+        @Override
+        public void freeMeterId(DeviceId deviceId, MeterId meterId) {
+
+        }
     }
 
 }
diff --git a/incubator/store/src/main/java/org/onosproject/incubator/store/meter/impl/DistributedMeterStore.java b/incubator/store/src/main/java/org/onosproject/incubator/store/meter/impl/DistributedMeterStore.java
index cf4bfcb..28cc7ba 100644
--- a/incubator/store/src/main/java/org/onosproject/incubator/store/meter/impl/DistributedMeterStore.java
+++ b/incubator/store/src/main/java/org/onosproject/incubator/store/meter/impl/DistributedMeterStore.java
@@ -433,6 +433,10 @@
 
     @Override
     public void freeMeterId(DeviceId deviceId, MeterId meterId) {
+        // Avoid to free meter not allocated
+        if (meterIdGenerators.get(deviceId) < meterId.id()) {
+            return;
+        }
         // Update the availability
         updateMeterIdAvailability(deviceId, meterId, true);
     }
diff --git a/incubator/store/src/test/java/org/onosproject/incubator/store/meter/impl/DistributedMeterStoreTest.java b/incubator/store/src/test/java/org/onosproject/incubator/store/meter/impl/DistributedMeterStoreTest.java
index f66d3ef..3c3727c 100644
--- a/incubator/store/src/test/java/org/onosproject/incubator/store/meter/impl/DistributedMeterStoreTest.java
+++ b/incubator/store/src/test/java/org/onosproject/incubator/store/meter/impl/DistributedMeterStoreTest.java
@@ -84,6 +84,7 @@
     // Meter ids used during the tests
     private MeterId mid1 = MeterId.meterId(1);
     private MeterId mid2 = MeterId.meterId(2);
+    private MeterId mid10 = MeterId.meterId(10);
 
     // Bands used during the tests
     private Band b1 = DefaultBand.builder()
@@ -209,6 +210,10 @@
         meterStore.freeMeterId(did1, mid1);
         // Allocate a meter id and verify is equal to mid1
         assertThat(mid1, is(meterStore.allocateMeterId(did1)));
+        // Free an id not allocated
+        meterStore.freeMeterId(did1, mid10);
+        // Allocate a meter id and verify is equal to mid2
+        assertThat(mid2, is(meterStore.allocateMeterId(did1)));
     }
 
     /**