blob: b94375cefa69e74c34edac928f84411ef561891d [file] [log] [blame]
alshabib7bb05012015-08-05 10:15:09 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
alshabib7bb05012015-08-05 10:15:09 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Thomas Vachuska52f2cd12018-11-08 21:20:04 -080016package org.onosproject.store.meter.impl;
alshabib7bb05012015-08-05 10:15:09 -070017
alshabibeadfc8e2015-08-18 15:40:46 -070018import com.google.common.collect.Collections2;
pierventre44220052020-09-22 12:51:06 +020019import com.google.common.collect.ImmutableSet;
Pier Luigif094c612017-10-14 12:15:02 +020020import com.google.common.collect.Iterables;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070021import com.google.common.collect.Lists;
alshabibeadfc8e2015-08-18 15:40:46 -070022import com.google.common.collect.Maps;
Pier Luigif094c612017-10-14 12:15:02 +020023import org.apache.commons.lang.math.RandomUtils;
Charles Chan593acf92017-11-22 13:55:41 -080024import org.onlab.util.KryoNamespace;
Daniele Moro43ac2892021-07-15 17:02:59 +020025import org.onosproject.core.ApplicationId;
Jordi Ortizaa8de492016-12-01 00:21:36 +010026import org.onosproject.net.DeviceId;
Pier Luigif094c612017-10-14 12:15:02 +020027import org.onosproject.net.behaviour.MeterQuery;
28import org.onosproject.net.driver.DriverHandler;
29import org.onosproject.net.driver.DriverService;
alshabib58fe6dc2015-08-19 17:16:13 -070030import org.onosproject.net.meter.Band;
31import org.onosproject.net.meter.DefaultBand;
alshabib10c810b2015-08-18 16:59:04 -070032import org.onosproject.net.meter.DefaultMeter;
Jordi Ortiz6c847762017-01-30 17:13:05 +010033import org.onosproject.net.meter.DefaultMeterFeatures;
alshabib10c810b2015-08-18 16:59:04 -070034import org.onosproject.net.meter.Meter;
Wailok Shumf013a782021-07-26 16:51:01 +080035import org.onosproject.net.meter.MeterCellId;
alshabib10c810b2015-08-18 16:59:04 -070036import org.onosproject.net.meter.MeterEvent;
37import org.onosproject.net.meter.MeterFailReason;
Jordi Ortizaa8de492016-12-01 00:21:36 +010038import org.onosproject.net.meter.MeterFeatures;
cansu.toprak409289d2017-10-27 10:04:05 +030039import org.onosproject.net.meter.MeterFeaturesFlag;
Jordi Ortiz6c847762017-01-30 17:13:05 +010040import org.onosproject.net.meter.MeterId;
alshabib70aaa1b2015-09-25 14:30:59 -070041import org.onosproject.net.meter.MeterKey;
alshabib10c810b2015-08-18 16:59:04 -070042import org.onosproject.net.meter.MeterOperation;
Wailok Shumf013a782021-07-26 16:51:01 +080043import org.onosproject.net.meter.MeterScope;
alshabib10c810b2015-08-18 16:59:04 -070044import org.onosproject.net.meter.MeterState;
45import org.onosproject.net.meter.MeterStore;
46import org.onosproject.net.meter.MeterStoreDelegate;
47import org.onosproject.net.meter.MeterStoreResult;
Wailok Shumf013a782021-07-26 16:51:01 +080048import org.onosproject.net.meter.MeterTableKey;
49import org.onosproject.net.pi.model.PiMeterId;
50import org.onosproject.net.pi.runtime.PiMeterCellId;
alshabib7bb05012015-08-05 10:15:09 -070051import org.onosproject.store.AbstractStore;
Pier Luigif094c612017-10-14 12:15:02 +020052import org.onosproject.store.primitives.DefaultDistributedSet;
alshabibeadfc8e2015-08-18 15:40:46 -070053import org.onosproject.store.serializers.KryoNamespaces;
Pier Luigif094c612017-10-14 12:15:02 +020054import org.onosproject.store.service.AtomicCounterMap;
alshabib7bb05012015-08-05 10:15:09 -070055import org.onosproject.store.service.ConsistentMap;
Pier Luigif094c612017-10-14 12:15:02 +020056import org.onosproject.store.service.DistributedPrimitive;
57import org.onosproject.store.service.DistributedSet;
Wailok Shumf013a782021-07-26 16:51:01 +080058import org.onosproject.store.service.EventuallyConsistentMap;
59import org.onosproject.store.service.EventuallyConsistentMapEvent;
60import org.onosproject.store.service.EventuallyConsistentMapListener;
alshabibeadfc8e2015-08-18 15:40:46 -070061import org.onosproject.store.service.MapEvent;
62import org.onosproject.store.service.MapEventListener;
alshabib7bb05012015-08-05 10:15:09 -070063import org.onosproject.store.service.Serializer;
alshabibeadfc8e2015-08-18 15:40:46 -070064import org.onosproject.store.service.StorageException;
alshabib7bb05012015-08-05 10:15:09 -070065import org.onosproject.store.service.StorageService;
alshabibeadfc8e2015-08-18 15:40:46 -070066import org.onosproject.store.service.Versioned;
Wailok Shumf013a782021-07-26 16:51:01 +080067import org.onosproject.store.service.WallClockTimestamp;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070068import org.osgi.service.component.annotations.Activate;
69import org.osgi.service.component.annotations.Component;
70import org.osgi.service.component.annotations.Deactivate;
71import org.osgi.service.component.annotations.Reference;
72import org.osgi.service.component.annotations.ReferenceCardinality;
alshabib7bb05012015-08-05 10:15:09 -070073import org.slf4j.Logger;
74
75import java.util.Collection;
Wailok Shumf013a782021-07-26 16:51:01 +080076import java.util.concurrent.ConcurrentMap;
77import java.util.concurrent.ConcurrentHashMap;
Gamze Abakaf57ef602019-03-11 06:52:48 +000078import java.util.List;
alshabibeadfc8e2015-08-18 15:40:46 -070079import java.util.Map;
Gamze Abakaf57ef602019-03-11 06:52:48 +000080import java.util.Objects;
Pier Luigif094c612017-10-14 12:15:02 +020081import java.util.Set;
alshabibeadfc8e2015-08-18 15:40:46 -070082import java.util.concurrent.CompletableFuture;
Pier Luigif094c612017-10-14 12:15:02 +020083import java.util.stream.Collectors;
alshabib7bb05012015-08-05 10:15:09 -070084
pierventre3b39bd82021-08-18 09:40:14 +020085import static com.google.common.base.Preconditions.checkArgument;
Thomas Vachuska52f2cd12018-11-08 21:20:04 -080086import static org.onosproject.store.meter.impl.DistributedMeterStore.ReuseStrategy.FIRST_FIT;
Jordi Ortizaa8de492016-12-01 00:21:36 +010087import static org.onosproject.net.meter.MeterFailReason.TIMEOUT;
Wailok Shumf013a782021-07-26 16:51:01 +080088import static org.onosproject.net.meter.MeterCellId.MeterCellType.INDEX;
89import static org.onosproject.net.meter.MeterCellId.MeterCellType.PIPELINE_INDEPENDENT;
Wailok Shum6a249352021-07-29 00:02:56 +080090import static org.onosproject.net.meter.MeterStoreResult.Type.FAIL;
alshabib7bb05012015-08-05 10:15:09 -070091import static org.slf4j.LoggerFactory.getLogger;
92
93/**
94 * A distributed meter store implementation. Meters are stored consistently
95 * across the cluster.
96 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070097@Component(immediate = true, service = MeterStore.class)
alshabib7bb05012015-08-05 10:15:09 -070098public class DistributedMeterStore extends AbstractStore<MeterEvent, MeterStoreDelegate>
99 implements MeterStore {
100
101 private Logger log = getLogger(getClass());
102
pierventre1b8afbc2020-07-13 14:07:05 +0200103 // Meters map related objects
alshabib7bb05012015-08-05 10:15:09 -0700104 private static final String METERSTORE = "onos-meter-store";
pierventre1b8afbc2020-07-13 14:07:05 +0200105 private ConsistentMap<MeterKey, MeterData> meters;
Wailok Shumf013a782021-07-26 16:51:01 +0800106 private MapEventListener<MeterKey, MeterData> metersMapListener = new InternalMetersMapEventListener();
pierventre44220052020-09-22 12:51:06 +0200107 private Map<MeterKey, MeterData> metersMap;
alshabib7bb05012015-08-05 10:15:09 -0700108
pierventre1b8afbc2020-07-13 14:07:05 +0200109 // Meters features related objects
110 private static final String METERFEATURESSTORE = "onos-meter-features-store";
Wailok Shumf013a782021-07-26 16:51:01 +0800111 private EventuallyConsistentMap<MeterTableKey, MeterFeatures> metersFeatures;
112 private EventuallyConsistentMapListener<MeterTableKey, MeterFeatures> featuresMapListener =
113 new InternalFeaturesMapEventListener();
pierventre1b8afbc2020-07-13 14:07:05 +0200114
115 // Meters id related objects
116 private static final String AVAILABLEMETERIDSTORE = "onos-meters-available-store";
pierventre3b39bd82021-08-18 09:40:14 +0200117 protected ConcurrentMap<MeterTableKey, DistributedSet<MeterKey>> availableMeterIds;
pierventre1b8afbc2020-07-13 14:07:05 +0200118 private static final String METERIDSTORE = "onos-meters-id-store";
Wailok Shumf013a782021-07-26 16:51:01 +0800119 private AtomicCounterMap<MeterTableKey> meterIdGenerators;
pierventre1b8afbc2020-07-13 14:07:05 +0200120
Charles Chan593acf92017-11-22 13:55:41 -0800121 private static final KryoNamespace.Builder APP_KRYO_BUILDER = KryoNamespace.newBuilder()
122 .register(KryoNamespaces.API)
123 .register(MeterKey.class)
124 .register(MeterData.class)
125 .register(DefaultMeter.class)
126 .register(DefaultBand.class)
127 .register(Band.Type.class)
128 .register(MeterState.class)
debmaiti1bea2892019-06-04 12:36:38 +0530129 .register(Meter.Unit.class)
pierventre3b39bd82021-08-18 09:40:14 +0200130 .register(MeterFailReason.class)
131 .register(MeterTableKey.class)
132 .register(MeterFeatures.class)
133 .register(DefaultMeterFeatures.class)
134 .register(MeterFeaturesFlag.class)
135 .register(MeterScope.class);
Charles Chan593acf92017-11-22 13:55:41 -0800136 private Serializer serializer = Serializer.using(Lists.newArrayList(APP_KRYO_BUILDER.build()));
137
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700138 @Reference(cardinality = ReferenceCardinality.MANDATORY)
alshabib7bb05012015-08-05 10:15:09 -0700139 private StorageService storageService;
140
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700141 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Pier Luigif094c612017-10-14 12:15:02 +0200142 protected DriverService driverService;
143
pierventre1b8afbc2020-07-13 14:07:05 +0200144 // Local cache to handle async ops through futures.
alshabib70aaa1b2015-09-25 14:30:59 -0700145 private Map<MeterKey, CompletableFuture<MeterStoreResult>> futures =
alshabibeadfc8e2015-08-18 15:40:46 -0700146 Maps.newConcurrentMap();
alshabib7bb05012015-08-05 10:15:09 -0700147
pierventre3b39bd82021-08-18 09:40:14 +0200148 // Control the user defined index mode for the store.
149 protected boolean userDefinedIndexMode = false;
150
Pier Luigif094c612017-10-14 12:15:02 +0200151 /**
152 * Defines possible selection strategies to reuse meter ids.
153 */
154 enum ReuseStrategy {
155 /**
156 * Select randomly an available id.
157 */
158 RANDOM,
159 /**
160 * Select the first one.
161 */
162 FIRST_FIT
163 }
Pier Luigif094c612017-10-14 12:15:02 +0200164 private ReuseStrategy reuseStrategy = FIRST_FIT;
Jordi Ortiz6c847762017-01-30 17:13:05 +0100165
alshabib7bb05012015-08-05 10:15:09 -0700166 @Activate
167 public void activate() {
alshabib70aaa1b2015-09-25 14:30:59 -0700168 meters = storageService.<MeterKey, MeterData>consistentMapBuilder()
alshabib7bb05012015-08-05 10:15:09 -0700169 .withName(METERSTORE)
Charles Chan593acf92017-11-22 13:55:41 -0800170 .withSerializer(serializer).build();
Wailok Shumf013a782021-07-26 16:51:01 +0800171 meters.addListener(metersMapListener);
pierventre44220052020-09-22 12:51:06 +0200172 metersMap = meters.asJavaMap();
pierventre26ac1512021-09-10 09:37:29 +0200173
Wailok Shumf013a782021-07-26 16:51:01 +0800174 metersFeatures = storageService.<MeterTableKey, MeterFeatures>eventuallyConsistentMapBuilder()
175 .withName(METERFEATURESSTORE)
176 .withTimestampProvider((key, features) -> new WallClockTimestamp())
pierventre3b39bd82021-08-18 09:40:14 +0200177 .withSerializer(APP_KRYO_BUILDER).build();
Wailok Shumf013a782021-07-26 16:51:01 +0800178 metersFeatures.addListener(featuresMapListener);
pierventre26ac1512021-09-10 09:37:29 +0200179
Wailok Shumf013a782021-07-26 16:51:01 +0800180 availableMeterIds = new ConcurrentHashMap<>();
pierventre26ac1512021-09-10 09:37:29 +0200181
Wailok Shumf013a782021-07-26 16:51:01 +0800182 meterIdGenerators = storageService.<MeterTableKey>atomicCounterMapBuilder()
Pier Luigif094c612017-10-14 12:15:02 +0200183 .withName(METERIDSTORE)
Wailok Shumf013a782021-07-26 16:51:01 +0800184 .withSerializer(Serializer.using(KryoNamespaces.API,
185 MeterTableKey.class,
186 MeterScope.class)).build();
pierventre3b39bd82021-08-18 09:40:14 +0200187
alshabib7bb05012015-08-05 10:15:09 -0700188 log.info("Started");
189 }
190
191 @Deactivate
192 public void deactivate() {
Wailok Shumf013a782021-07-26 16:51:01 +0800193 meters.removeListener(metersMapListener);
194 metersFeatures.removeListener(featuresMapListener);
195 meters.destroy();
196 metersFeatures.destroy();
pierventre3b39bd82021-08-18 09:40:14 +0200197 availableMeterIds.forEach((key, set) -> set.destroy());
198
alshabib7bb05012015-08-05 10:15:09 -0700199 log.info("Stopped");
200 }
201
alshabib7bb05012015-08-05 10:15:09 -0700202 @Override
Wailok Shum6a249352021-07-29 00:02:56 +0800203 public CompletableFuture<MeterStoreResult> addOrUpdateMeter(Meter meter) {
pierventre3b39bd82021-08-18 09:40:14 +0200204 checkArgument(validIndex(meter), "Meter index is not valid");
Wailok Shum6a249352021-07-29 00:02:56 +0800205 CompletableFuture<MeterStoreResult> future = new CompletableFuture<>();
206 MeterKey key = MeterKey.key(meter.deviceId(), meter.meterCellId());
207 MeterData data = new MeterData(meter, null);
Wailok Shum6a249352021-07-29 00:02:56 +0800208 futures.put(key, future);
Wailok Shum6a249352021-07-29 00:02:56 +0800209 try {
210 meters.compute(key, (k, v) -> data);
211 } catch (StorageException e) {
212 log.error("{} thrown a storage exception: {}", e.getStackTrace()[0].getMethodName(),
213 e.getMessage(), e);
214 futures.remove(key);
215 future.completeExceptionally(e);
216 }
217 return future;
218 }
219
220 @Override
alshabibeadfc8e2015-08-18 15:40:46 -0700221 public CompletableFuture<MeterStoreResult> deleteMeter(Meter meter) {
222 CompletableFuture<MeterStoreResult> future = new CompletableFuture<>();
Wailok Shum6a249352021-07-29 00:02:56 +0800223 MeterKey key = MeterKey.key(meter.deviceId(), meter.meterCellId());
alshabib70aaa1b2015-09-25 14:30:59 -0700224 futures.put(key, future);
Pier Luigif094c612017-10-14 12:15:02 +0200225 // Update the state of the meter. It will be pruned by observing
alshabib7bb05012015-08-05 10:15:09 -0700226 // that it has been removed from the dataplane.
alshabibeadfc8e2015-08-18 15:40:46 -0700227 try {
pierventre26ac1512021-09-10 09:37:29 +0200228 Versioned<MeterData> versionedData = meters.computeIfPresent(key, (k, v) -> {
229 DefaultMeter m = (DefaultMeter) v.meter();
230 MeterState meterState = m.state();
231 if (meterState == MeterState.PENDING_REMOVE) {
232 return v;
233 }
234 m.setState(meter.state());
235 return new MeterData(m, v.reason().isPresent() ? v.reason().get() : null);
236 });
237 // If it does not exist in the system, completes immediately
238 if (versionedData == null) {
239 futures.remove(key);
alshabibe1248b62015-08-20 17:21:55 -0700240 future.complete(MeterStoreResult.success());
241 }
alshabibeadfc8e2015-08-18 15:40:46 -0700242 } catch (StorageException e) {
pierventre1b8afbc2020-07-13 14:07:05 +0200243 log.error("{} thrown a storage exception: {}", e.getStackTrace()[0].getMethodName(),
244 e.getMessage(), e);
Hwanwook Lee8206ad92018-01-02 18:03:50 +0900245 futures.remove(key);
alshabibeadfc8e2015-08-18 15:40:46 -0700246 future.completeExceptionally(e);
alshabib7bb05012015-08-05 10:15:09 -0700247 }
alshabibeadfc8e2015-08-18 15:40:46 -0700248 return future;
alshabib7bb05012015-08-05 10:15:09 -0700249 }
250
251 @Override
Jordi Ortizaa8de492016-12-01 00:21:36 +0100252 public MeterStoreResult storeMeterFeatures(MeterFeatures meterfeatures) {
Wailok Shumf013a782021-07-26 16:51:01 +0800253 // Store meter features, this is done once for each features of every device
Jordi Ortizaa8de492016-12-01 00:21:36 +0100254 MeterStoreResult result = MeterStoreResult.success();
Wailok Shumf013a782021-07-26 16:51:01 +0800255 MeterTableKey key = MeterTableKey.key(meterfeatures.deviceId(), meterfeatures.scope());
Jordi Ortizaa8de492016-12-01 00:21:36 +0100256 try {
Wailok Shumf013a782021-07-26 16:51:01 +0800257 metersFeatures.put(key, meterfeatures);
Jordi Ortizaa8de492016-12-01 00:21:36 +0100258 } catch (StorageException e) {
pierventre1b8afbc2020-07-13 14:07:05 +0200259 log.error("{} thrown a storage exception: {}", e.getStackTrace()[0].getMethodName(),
260 e.getMessage(), e);
Jordi Ortizaa8de492016-12-01 00:21:36 +0100261 result = MeterStoreResult.fail(TIMEOUT);
262 }
263 return result;
264 }
265
266 @Override
Wailok Shum6a249352021-07-29 00:02:56 +0800267 public MeterStoreResult storeMeterFeatures(Collection<MeterFeatures> meterfeatures) {
268 // These store operations is treated as one single operation
269 // If one of them is failed, Fail is returned
270 // But the failed operation will not block the rest.
271 MeterStoreResult result = MeterStoreResult.success();
272 for (MeterFeatures mf : meterfeatures) {
273 if (storeMeterFeatures(mf).type() == FAIL) {
274 result = MeterStoreResult.fail(TIMEOUT);
275 }
276 }
277 return result;
278 }
279
280 @Override
Jordi Ortizaa8de492016-12-01 00:21:36 +0100281 public MeterStoreResult deleteMeterFeatures(DeviceId deviceId) {
282 MeterStoreResult result = MeterStoreResult.success();
Jordi Ortizaa8de492016-12-01 00:21:36 +0100283 try {
Wailok Shumf013a782021-07-26 16:51:01 +0800284 Set<MeterTableKey> keys = metersFeatures.keySet().stream()
285 .filter(key -> key.deviceId().equals(deviceId))
286 .collect(Collectors.toUnmodifiableSet());
pierventre3b39bd82021-08-18 09:40:14 +0200287 keys.forEach(k -> metersFeatures.remove(k));
Jordi Ortizaa8de492016-12-01 00:21:36 +0100288 } catch (StorageException e) {
pierventre1b8afbc2020-07-13 14:07:05 +0200289 log.error("{} thrown a storage exception: {}", e.getStackTrace()[0].getMethodName(),
Wailok Shumf013a782021-07-26 16:51:01 +0800290 e.getMessage(), e);
Jordi Ortizaa8de492016-12-01 00:21:36 +0100291 result = MeterStoreResult.fail(TIMEOUT);
292 }
293 return result;
294 }
295
296 @Override
Wailok Shum6a249352021-07-29 00:02:56 +0800297 public MeterStoreResult deleteMeterFeatures(Collection<MeterFeatures> meterfeatures) {
pierventre26ac1512021-09-10 09:37:29 +0200298 // Same logic of storeMeterFeatures
Wailok Shum6a249352021-07-29 00:02:56 +0800299 MeterStoreResult result = MeterStoreResult.success();
300 for (MeterFeatures mf : meterfeatures) {
301 try {
302 MeterTableKey key = MeterTableKey.key(mf.deviceId(), mf.scope());
303 metersFeatures.remove(key);
304 } catch (StorageException e) {
305 log.error("{} thrown a storage exception: {}", e.getStackTrace()[0].getMethodName(),
306 e.getMessage(), e);
307 result = MeterStoreResult.fail(TIMEOUT);
308 }
309 }
310
311 return result;
312 }
313
314 @Override
pierventre44220052020-09-22 12:51:06 +0200315 public Meter updateMeterState(Meter meter) {
pierventre1b8afbc2020-07-13 14:07:05 +0200316 // Update meter if present (stats workflow)
Wailok Shum6a249352021-07-29 00:02:56 +0800317 MeterKey key = MeterKey.key(meter.deviceId(), meter.meterCellId());
pierventre44220052020-09-22 12:51:06 +0200318 Versioned<MeterData> value = meters.computeIfPresent(key, (k, v) -> {
alshabibeadfc8e2015-08-18 15:40:46 -0700319 DefaultMeter m = (DefaultMeter) v.meter();
pier59721bf2020-01-08 08:57:46 +0100320 MeterState meterState = m.state();
321 if (meterState == MeterState.PENDING_ADD) {
322 m.setState(meter.state());
323 }
alshabib7bb05012015-08-05 10:15:09 -0700324 m.setProcessedPackets(meter.packetsSeen());
325 m.setProcessedBytes(meter.bytesSeen());
326 m.setLife(meter.life());
alshabibeadfc8e2015-08-18 15:40:46 -0700327 // TODO: Prune if drops to zero.
alshabib7bb05012015-08-05 10:15:09 -0700328 m.setReferenceCount(meter.referenceCount());
pierventre1b8afbc2020-07-13 14:07:05 +0200329 return new MeterData(m, null);
alshabib7bb05012015-08-05 10:15:09 -0700330 });
pierventre44220052020-09-22 12:51:06 +0200331 return value != null ? value.value().meter() : null;
alshabib7bb05012015-08-05 10:15:09 -0700332 }
333
334 @Override
alshabib70aaa1b2015-09-25 14:30:59 -0700335 public Meter getMeter(MeterKey key) {
336 MeterData data = Versioned.valueOrElse(meters.get(key), null);
alshabibeadfc8e2015-08-18 15:40:46 -0700337 return data == null ? null : data.meter();
alshabib7bb05012015-08-05 10:15:09 -0700338 }
339
340 @Override
341 public Collection<Meter> getAllMeters() {
pierventre44220052020-09-22 12:51:06 +0200342 return Collections2.transform(ImmutableSet.copyOf(metersMap.values()),
alshabibeadfc8e2015-08-18 15:40:46 -0700343 MeterData::meter);
alshabib7bb05012015-08-05 10:15:09 -0700344 }
345
346 @Override
Jordi Ortiz9287b632017-06-22 11:01:37 +0200347 public Collection<Meter> getAllMeters(DeviceId deviceId) {
348 return Collections2.transform(
pierventre44220052020-09-22 12:51:06 +0200349 Collections2.filter(ImmutableSet.copyOf(metersMap.values()),
Jordi Ortiz9287b632017-06-22 11:01:37 +0200350 (MeterData m) -> m.meter().deviceId().equals(deviceId)),
351 MeterData::meter);
352 }
353
354 @Override
pierventrec0914ec2021-08-27 15:25:02 +0200355 public Collection<Meter> getAllMeters(DeviceId deviceId, MeterScope scope) {
356 if (scope.equals(MeterScope.globalScope())) {
357 return Collections2.transform(
358 Collections2.filter(ImmutableSet.copyOf(metersMap.values()),
359 (MeterData m) -> m.meter().meterCellId().type() == INDEX),
360 MeterData::meter);
361 }
362 return Collections2.transform(
363 Collections2.filter(ImmutableSet.copyOf(metersMap.values()),
364 (MeterData m) -> m.meter().meterCellId().type() == PIPELINE_INDEPENDENT &&
365 ((PiMeterCellId) m.meter().meterCellId()).meterId().id().equals(scope.id())),
366 MeterData::meter);
367 }
368
369 @Override
alshabib7bb05012015-08-05 10:15:09 -0700370 public void failedMeter(MeterOperation op, MeterFailReason reason) {
pierventre1b8afbc2020-07-13 14:07:05 +0200371 // Meter ops failed (got notification from the sb)
pierventre3b39bd82021-08-18 09:40:14 +0200372 MeterKey key = MeterKey.key(op.meter().deviceId(), op.meter().meterCellId());
pierventre1b8afbc2020-07-13 14:07:05 +0200373 meters.computeIfPresent(key, (k, v) -> new MeterData(v.meter(), reason));
alshabib7bb05012015-08-05 10:15:09 -0700374 }
375
alshabib5eb79392015-08-19 18:09:55 -0700376 @Override
Wailok Shumf013a782021-07-26 16:51:01 +0800377 public void purgeMeter(Meter m) {
pierventre26ac1512021-09-10 09:37:29 +0200378 // Once we receive the ack from the sb, create the key
379 // remove definitely the meter and free the id
pierventre3b39bd82021-08-18 09:40:14 +0200380 MeterKey key = MeterKey.key(m.deviceId(), m.meterCellId());
pierventre1b8afbc2020-07-13 14:07:05 +0200381 try {
382 if (Versioned.valueOrNull(meters.remove(key)) != null) {
Wailok Shumf013a782021-07-26 16:51:01 +0800383 MeterScope scope;
384 if (m.meterCellId().type() == PIPELINE_INDEPENDENT) {
385 PiMeterCellId piMeterCellId = (PiMeterCellId) m.meterCellId();
386 scope = MeterScope.of(piMeterCellId.meterId().id());
387 } else {
388 scope = MeterScope.globalScope();
389 }
390 MeterTableKey meterTableKey = MeterTableKey.key(m.deviceId(), scope);
391 freeMeterId(meterTableKey, m.meterCellId());
pierventre1b8afbc2020-07-13 14:07:05 +0200392 }
393 } catch (StorageException e) {
394 log.error("{} thrown a storage exception: {}", e.getStackTrace()[0].getMethodName(),
395 e.getMessage(), e);
pier59721bf2020-01-08 08:57:46 +0100396 }
alshabib5eb79392015-08-19 18:09:55 -0700397 }
398
Jordi Ortizaa8de492016-12-01 00:21:36 +0100399 @Override
pierventre3b39bd82021-08-18 09:40:14 +0200400 public void purgeMeters(DeviceId deviceId) {
Gamze Abakaf57ef602019-03-11 06:52:48 +0000401 List<Versioned<MeterData>> metersPendingRemove = meters.stream()
402 .filter(e -> Objects.equals(e.getKey().deviceId(), deviceId))
403 .map(Map.Entry::getValue)
404 .collect(Collectors.toList());
Gamze Abakaf57ef602019-03-11 06:52:48 +0000405 metersPendingRemove.forEach(versionedMeterKey
Wailok Shumf013a782021-07-26 16:51:01 +0800406 -> purgeMeter(versionedMeterKey.value().meter()));
Gamze Abakaf57ef602019-03-11 06:52:48 +0000407 }
408
409 @Override
Daniele Moro43ac2892021-07-15 17:02:59 +0200410 public void purgeMeters(DeviceId deviceId, ApplicationId appId) {
411 List<Versioned<MeterData>> metersPendingRemove = meters.stream()
412 .filter(e -> Objects.equals(e.getKey().deviceId(), deviceId) &&
413 e.getValue().value().meter().appId().equals(appId))
414 .map(Map.Entry::getValue)
415 .collect(Collectors.toList());
pierventre3b39bd82021-08-18 09:40:14 +0200416 metersPendingRemove.forEach(versionedMeterKey
417 -> purgeMeter(versionedMeterKey.value().meter()));
418 }
419
420 @Override
421 public boolean userDefinedIndexMode(boolean enable) {
422 if (meters.isEmpty() && meterIdGenerators.isEmpty()) {
423 userDefinedIndexMode = enable;
424 } else {
425 log.warn("Unable to {} user defined index mode as store did" +
426 "already some allocations", enable ? "activate" : "deactivate");
427 }
428 return userDefinedIndexMode;
Daniele Moro43ac2892021-07-15 17:02:59 +0200429 }
430
pierventre26ac1512021-09-10 09:37:29 +0200431 protected long getMaxMeters(MeterTableKey key) {
Wailok Shumf013a782021-07-26 16:51:01 +0800432 MeterFeatures features = metersFeatures.get(key);
Jordi Ortizaa8de492016-12-01 00:21:36 +0100433 return features == null ? 0L : features.maxMeter();
434 }
435
pierventre26ac1512021-09-10 09:37:29 +0200436 // Validate index using the meter features, useful mainly
437 // when user defined index mode is enabled
pierventre3b39bd82021-08-18 09:40:14 +0200438 private boolean validIndex(Meter meter) {
439 long index;
440 MeterTableKey key;
441
442 if (meter.meterCellId().type() == PIPELINE_INDEPENDENT) {
443 PiMeterCellId piMeterCellId = (PiMeterCellId) meter.meterCellId();
444 index = piMeterCellId.index();
445 key = MeterTableKey.key(meter.deviceId(), MeterScope.of(piMeterCellId.meterId().id()));
446 } else if (meter.meterCellId().type() == INDEX) {
447 MeterId meterId = (MeterId) meter.meterCellId();
448 index = meterId.id();
449 key = MeterTableKey.key(meter.deviceId(), MeterScope.globalScope());
450 } else {
pierventre26ac1512021-09-10 09:37:29 +0200451 log.warn("Unable to validate index unsupported cell type {}", meter.meterCellId().type());
pierventre3b39bd82021-08-18 09:40:14 +0200452 return false;
453 }
454
455 MeterFeatures features = metersFeatures.get(key);
456 long startIndex = features == null ? -1L : features.startIndex();
457 long endIndex = features == null ? -1L : features.endIndex();
458 return index >= startIndex && index <= endIndex;
459 }
460
Wailok Shumf013a782021-07-26 16:51:01 +0800461 private long getStartIndex(MeterTableKey key) {
Wailok Shumf013a782021-07-26 16:51:01 +0800462 MeterFeatures features = metersFeatures.get(key);
463 return features == null ? -1L : features.startIndex();
464 }
465
466 private long getEndIndex(MeterTableKey key) {
Wailok Shumf013a782021-07-26 16:51:01 +0800467 MeterFeatures features = metersFeatures.get(key);
468 return features == null ? -1L : features.endIndex();
469 }
470
pierventre26ac1512021-09-10 09:37:29 +0200471 // queryMaxMeters is implemented in MeterQuery behaviour implementations.
Pier Luigif094c612017-10-14 12:15:02 +0200472 private long queryMaxMeters(DeviceId device) {
Pier Luigif094c612017-10-14 12:15:02 +0200473 DriverHandler handler = driverService.createHandler(device);
Pier Luigif094c612017-10-14 12:15:02 +0200474 if (handler == null || !handler.hasBehaviour(MeterQuery.class)) {
Pier Luigif094c612017-10-14 12:15:02 +0200475 return 0L;
476 }
pierventre26ac1512021-09-10 09:37:29 +0200477
478 // FIXME architecturally this is not right, we should fallback to this
479 // behavior in the providers. Once we do that we can remove this code.
Pier Luigif094c612017-10-14 12:15:02 +0200480 MeterQuery query = handler.behaviour(MeterQuery.class);
pierventre26ac1512021-09-10 09:37:29 +0200481 // This results to be necessary because the available ids sets are created
482 // in the meter features map listener if the device does not provide the meter
483 // feature this is the only chance to create this set.
Wailok Shumf013a782021-07-26 16:51:01 +0800484 String setName = AVAILABLEMETERIDSTORE + "-" + device + "global";
485 MeterTableKey meterTableKey = MeterTableKey.key(device, MeterScope.globalScope());
486 insertAvailableKeySet(meterTableKey, setName);
pierventre26ac1512021-09-10 09:37:29 +0200487
Pier Luigif094c612017-10-14 12:15:02 +0200488 return query.getMaxMeters();
489 }
490
Wailok Shumf013a782021-07-26 16:51:01 +0800491 private boolean updateMeterIdAvailability(MeterTableKey meterTableKey, MeterCellId id,
Pier Luigif094c612017-10-14 12:15:02 +0200492 boolean available) {
Wailok Shumf013a782021-07-26 16:51:01 +0800493 DistributedSet<MeterKey> keySet = availableMeterIds.get(meterTableKey);
494 if (keySet == null) {
Wailok Shumf013a782021-07-26 16:51:01 +0800495 log.warn("Reusable Key set for device: {} scope: {} not found",
496 meterTableKey.deviceId(), meterTableKey.scope());
497 return false;
498 }
pierventre26ac1512021-09-10 09:37:29 +0200499
Pier Luigif094c612017-10-14 12:15:02 +0200500 // According to available, make available or unavailable a meter key
Wailok Shumf013a782021-07-26 16:51:01 +0800501 DeviceId deviceId = meterTableKey.deviceId();
502 return available ? keySet.add(MeterKey.key(deviceId, id)) :
503 keySet.remove(MeterKey.key(deviceId, id));
Pier Luigif094c612017-10-14 12:15:02 +0200504 }
505
Wailok Shumf013a782021-07-26 16:51:01 +0800506 private MeterCellId getNextAvailableId(Set<MeterCellId> availableIds) {
Pier Luigif094c612017-10-14 12:15:02 +0200507 if (availableIds.isEmpty()) {
Pier Luigif094c612017-10-14 12:15:02 +0200508 return null;
509 }
pierventre26ac1512021-09-10 09:37:29 +0200510
Pier Luigif094c612017-10-14 12:15:02 +0200511 if (reuseStrategy == FIRST_FIT || availableIds.size() == 1) {
512 return availableIds.iterator().next();
513 }
pierventre26ac1512021-09-10 09:37:29 +0200514
515 // If it is random, get the size and return a random element
Pier Luigif094c612017-10-14 12:15:02 +0200516 int size = availableIds.size();
Pier Luigif094c612017-10-14 12:15:02 +0200517 return Iterables.get(availableIds, RandomUtils.nextInt(size));
518 }
519
pierventre26ac1512021-09-10 09:37:29 +0200520 // Implements reuse strategy of the meter cell ids
Wailok Shumf013a782021-07-26 16:51:01 +0800521 private MeterCellId firstReusableMeterId(MeterTableKey meterTableKey) {
Wailok Shumf013a782021-07-26 16:51:01 +0800522 DistributedSet<MeterKey> keySet = availableMeterIds.get(meterTableKey);
523 if (keySet == null) {
Wailok Shumf013a782021-07-26 16:51:01 +0800524 log.warn("Reusable Key set for device: {} scope: {} not found",
525 meterTableKey.deviceId(), meterTableKey.scope());
526 return null;
527 }
pierventre26ac1512021-09-10 09:37:29 +0200528
Wailok Shumf013a782021-07-26 16:51:01 +0800529 Set<MeterCellId> localAvailableMeterIds = keySet.stream()
530 .filter(meterKey ->
531 meterKey.deviceId().equals(meterTableKey.deviceId()))
pierventre3b39bd82021-08-18 09:40:14 +0200532 .map(MeterKey::meterCellId)
Pier Luigif094c612017-10-14 12:15:02 +0200533 .collect(Collectors.toSet());
Wailok Shumf013a782021-07-26 16:51:01 +0800534 MeterCellId meterId = getNextAvailableId(localAvailableMeterIds);
Pier Luigif094c612017-10-14 12:15:02 +0200535 while (meterId != null) {
Wailok Shumf013a782021-07-26 16:51:01 +0800536 if (updateMeterIdAvailability(meterTableKey, meterId, false)) {
Pier Luigif094c612017-10-14 12:15:02 +0200537 return meterId;
538 }
Pier Luigif094c612017-10-14 12:15:02 +0200539 localAvailableMeterIds.remove(meterId);
Pier Luigif094c612017-10-14 12:15:02 +0200540 meterId = getNextAvailableId(localAvailableMeterIds);
541 }
pierventre26ac1512021-09-10 09:37:29 +0200542 // there are no available ids that can be reused
Pier Luigif094c612017-10-14 12:15:02 +0200543 return null;
544 }
545
546 @Override
Wailok Shumf013a782021-07-26 16:51:01 +0800547 public MeterCellId allocateMeterId(DeviceId deviceId, MeterScope meterScope) {
pierventre3b39bd82021-08-18 09:40:14 +0200548 if (userDefinedIndexMode) {
549 log.warn("Unable to allocate meter id when user defined index mode is enabled");
550 return null;
551 }
Wailok Shumf013a782021-07-26 16:51:01 +0800552 MeterTableKey meterTableKey = MeterTableKey.key(deviceId, meterScope);
553 MeterCellId meterCellId;
Pier Luigif094c612017-10-14 12:15:02 +0200554 long id;
Wailok Shumf013a782021-07-26 16:51:01 +0800555 // First, search for reusable key
556 meterCellId = firstReusableMeterId(meterTableKey);
557 if (meterCellId != null) {
Wailok Shumf013a782021-07-26 16:51:01 +0800558 return meterCellId;
Pier Luigif094c612017-10-14 12:15:02 +0200559 }
Wailok Shumf013a782021-07-26 16:51:01 +0800560 // If there was no reusable meter id we have to generate a new value
561 // using start and end index as lower and upper bound respectively.
562 long startIndex = getStartIndex(meterTableKey);
563 long endIndex = getEndIndex(meterTableKey);
pierventre26ac1512021-09-10 09:37:29 +0200564 // If the device does not give us MeterFeatures fallback to queryMeters
Wailok Shumf013a782021-07-26 16:51:01 +0800565 if (startIndex == -1L || endIndex == -1L) {
pierventre26ac1512021-09-10 09:37:29 +0200566 // Only meaningful for OpenFlow today
Wailok Shumf013a782021-07-26 16:51:01 +0800567 long maxMeters = queryMaxMeters(deviceId);
Wailok Shumf013a782021-07-26 16:51:01 +0800568 if (maxMeters == 0L) {
569 return null;
570 } else {
Wailok Shum90b988a2021-09-13 17:23:20 +0800571 // OpenFlow meter index starts from 1, ends with max
Wailok Shumf013a782021-07-26 16:51:01 +0800572 startIndex = 1L;
Wailok Shum90b988a2021-09-13 17:23:20 +0800573 endIndex = maxMeters;
Wailok Shumf013a782021-07-26 16:51:01 +0800574 }
Pier Luigif094c612017-10-14 12:15:02 +0200575 }
pierventre26ac1512021-09-10 09:37:29 +0200576
Wailok Shumf013a782021-07-26 16:51:01 +0800577 do {
Wailok Shum79919522021-08-22 19:35:34 +0800578 id = meterIdGenerators.getAndIncrement(meterTableKey);
Wailok Shumf013a782021-07-26 16:51:01 +0800579 } while (id < startIndex);
Wailok Shumf013a782021-07-26 16:51:01 +0800580 if (id > endIndex) {
Pier Luigif094c612017-10-14 12:15:02 +0200581 return null;
582 }
pierventre26ac1512021-09-10 09:37:29 +0200583
584 // For backward compatibility if we are using global scope,
585 // return a MeterId, otherwise we create a PiMeterCellId
Wailok Shumf013a782021-07-26 16:51:01 +0800586 if (meterScope.isGlobal()) {
587 return MeterId.meterId(id);
588 } else {
589 return PiMeterCellId.ofIndirect(PiMeterId.of(meterScope.id()), id);
590 }
591
Pier Luigif094c612017-10-14 12:15:02 +0200592 }
593
594 @Override
595 public void freeMeterId(DeviceId deviceId, MeterId meterId) {
Wailok Shumf013a782021-07-26 16:51:01 +0800596 MeterTableKey meterTableKey = MeterTableKey.key(deviceId, MeterScope.globalScope());
597 freeMeterId(meterTableKey, meterId);
598 }
599
pierventre26ac1512021-09-10 09:37:29 +0200600 protected void freeMeterId(DeviceId deviceId, MeterCellId meterCellId) {
601 MeterTableKey meterTableKey;
602 if (meterCellId.type() == PIPELINE_INDEPENDENT) {
603 meterTableKey = MeterTableKey.key(deviceId,
604 MeterScope.of(((PiMeterCellId) meterCellId).meterId().id()));
605 } else if (meterCellId.type() == INDEX) {
606 meterTableKey = MeterTableKey.key(deviceId, MeterScope.globalScope());
607 } else {
608 log.warn("Unable to free meter id unsupported cell type {}", meterCellId.type());
609 return;
610 }
611 freeMeterId(meterTableKey, meterCellId);
612 }
613
614 protected void freeMeterId(MeterTableKey meterTableKey, MeterCellId meterCellId) {
pierventre3b39bd82021-08-18 09:40:14 +0200615 if (userDefinedIndexMode) {
pierventre26ac1512021-09-10 09:37:29 +0200616 log.debug("Unable to free meter id when user defined index mode is enabled");
pierventre3b39bd82021-08-18 09:40:14 +0200617 return;
618 }
Wailok Shumf013a782021-07-26 16:51:01 +0800619 long index;
620 if (meterCellId.type() == PIPELINE_INDEPENDENT) {
621 PiMeterCellId piMeterCellId = (PiMeterCellId) meterCellId;
622 index = piMeterCellId.index();
623 } else if (meterCellId.type() == INDEX) {
624 MeterId meterId = (MeterId) meterCellId;
625 index = meterId.id();
626 } else {
pierventre26ac1512021-09-10 09:37:29 +0200627 log.warn("Unable to free meter id unsupported cell type {}", meterCellId.type());
Wailok Shumf013a782021-07-26 16:51:01 +0800628 return;
629 }
Pier Luigibdcd9672017-10-13 13:54:48 +0200630 // Avoid to free meter not allocated
Wailok Shum79919522021-08-22 19:35:34 +0800631 if (meterIdGenerators.get(meterTableKey) <= index) {
Pier Luigibdcd9672017-10-13 13:54:48 +0200632 return;
633 }
Wailok Shumf013a782021-07-26 16:51:01 +0800634 updateMeterIdAvailability(meterTableKey, meterCellId, true);
Pier Luigif094c612017-10-14 12:15:02 +0200635 }
636
pierventre1b8afbc2020-07-13 14:07:05 +0200637 // Enabling the events distribution across the cluster
Wailok Shumf013a782021-07-26 16:51:01 +0800638 private class InternalMetersMapEventListener implements MapEventListener<MeterKey, MeterData> {
alshabibeadfc8e2015-08-18 15:40:46 -0700639 @Override
HIGUCHI Yuta0574a552015-09-29 14:38:25 -0700640 public void event(MapEvent<MeterKey, MeterData> event) {
641 MeterKey key = event.key();
Ray Milkeyd0f017f2018-09-21 12:52:34 -0700642 Versioned<MeterData> value = event.type() == MapEvent.Type.REMOVE ? event.oldValue() : event.newValue();
643 MeterData data = value.value();
pierventre1b8afbc2020-07-13 14:07:05 +0200644 MeterData oldData = Versioned.valueOrNull(event.oldValue());
alshabibeadfc8e2015-08-18 15:40:46 -0700645 switch (event.type()) {
646 case INSERT:
647 case UPDATE:
648 switch (data.meter().state()) {
649 case PENDING_ADD:
650 case PENDING_REMOVE:
pierventre1b8afbc2020-07-13 14:07:05 +0200651 // Two cases. If there is a reason, the meter operation failed.
652 // Otherwise, we are ready to install/remove through the delegate.
653 if (data.reason().isEmpty()) {
654 notifyDelegate(new MeterEvent(data.meter().state() == MeterState.PENDING_ADD ?
655 MeterEvent.Type.METER_ADD_REQ : MeterEvent.Type.METER_REM_REQ, data.meter()));
656 } else {
Jordi Ortizdf28ecd2017-03-25 19:22:36 +0100657 futures.computeIfPresent(key, (k, v) -> {
pierventre1b8afbc2020-07-13 14:07:05 +0200658 v.complete(MeterStoreResult.fail(data.reason().get()));
Jordi Ortizdf28ecd2017-03-25 19:22:36 +0100659 return null;
660 });
alshabibe1248b62015-08-20 17:21:55 -0700661 }
662 break;
pierventre1b8afbc2020-07-13 14:07:05 +0200663 case ADDED:
664 // Transition from pending to installed
665 if (data.meter().state() == MeterState.ADDED &&
666 (oldData != null && oldData.meter().state() == MeterState.PENDING_ADD)) {
667 futures.computeIfPresent(key, (k, v) -> {
668 v.complete(MeterStoreResult.success());
669 return null;
670 });
671 notifyDelegate(new MeterEvent(MeterEvent.Type.METER_ADDED, data.meter()));
pierventrec0914ec2021-08-27 15:25:02 +0200672 // Update stats case - we report reference count zero only for INDEX based meters
673 } else if (data.meter().referenceCount() == 0 &&
674 data.meter().meterCellId().type() == INDEX) {
pierventre1b8afbc2020-07-13 14:07:05 +0200675 notifyDelegate(new MeterEvent(MeterEvent.Type.METER_REFERENCE_COUNT_ZERO,
676 data.meter()));
alshabibeadfc8e2015-08-18 15:40:46 -0700677 }
678 break;
679 default:
680 log.warn("Unknown meter state type {}", data.meter().state());
681 }
682 break;
683 case REMOVE:
pierventre1b8afbc2020-07-13 14:07:05 +0200684 futures.computeIfPresent(key, (k, v) -> {
685 v.complete(MeterStoreResult.success());
686 return null;
687 });
pierventre1b8afbc2020-07-13 14:07:05 +0200688 notifyDelegate(new MeterEvent(MeterEvent.Type.METER_REMOVED, data.meter()));
alshabibeadfc8e2015-08-18 15:40:46 -0700689 break;
690 default:
691 log.warn("Unknown Map event type {}", event.type());
692 }
alshabibeadfc8e2015-08-18 15:40:46 -0700693 }
694 }
695
Wailok Shumf013a782021-07-26 16:51:01 +0800696 private class InternalFeaturesMapEventListener implements
697 EventuallyConsistentMapListener<MeterTableKey, MeterFeatures> {
698 @Override
699 public void event(EventuallyConsistentMapEvent<MeterTableKey, MeterFeatures> event) {
700 MeterTableKey meterTableKey = event.key();
701 MeterFeatures meterFeatures = event.value();
702 switch (event.type()) {
703 case PUT:
Wailok Shumf013a782021-07-26 16:51:01 +0800704 String setName = AVAILABLEMETERIDSTORE + "-" +
705 meterFeatures.deviceId() + meterFeatures.scope().id();
706 insertAvailableKeySet(meterTableKey, setName);
707 break;
708 case REMOVE:
Wailok Shumf013a782021-07-26 16:51:01 +0800709 DistributedSet<MeterKey> set = availableMeterIds.remove(meterTableKey);
710 if (set != null) {
711 set.destroy();
712 }
713 break;
714 default:
715 break;
716 }
717 }
718 }
719
720 private void insertAvailableKeySet(MeterTableKey meterTableKey, String setName) {
721 DistributedSet<MeterKey> availableMeterIdSet =
722 new DefaultDistributedSet<>(storageService.<MeterKey>setBuilder()
723 .withName(setName)
724 .withSerializer(Serializer.using(KryoNamespaces.API,
725 MeterKey.class)).build(),
726 DistributedPrimitive.DEFAULT_OPERATION_TIMEOUT_MILLIS);
727 availableMeterIds.put(meterTableKey, availableMeterIdSet);
728 }
alshabib7bb05012015-08-05 10:15:09 -0700729}