blob: 9b34c839a393d25e7da2dc7fa38b5b9ca9cebc79 [file] [log] [blame]
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001/*
Joey Armstrong7f6d6d22023-01-09 17:09:50 -05002 * Copyright 2021-2023 Open Networking Foundation (ONF) and the ONF Contributors
Andrea Campanellacbbb7952019-11-25 06:38:41 +00003 *
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 */
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070016
Andrea Campanellacbbb7952019-11-25 06:38:41 +000017package org.opencord.olt.impl;
18
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070019import com.google.common.collect.ImmutableMap;
Gustavo Silva3b5a5512022-08-29 16:18:57 -030020import org.onlab.util.Identifier;
Jonathan Hart4f178fa2020-02-03 10:46:01 -080021import org.onlab.util.KryoNamespace;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000022import org.onlab.util.Tools;
23import org.onosproject.cfg.ComponentConfigService;
24import org.onosproject.core.ApplicationId;
25import org.onosproject.core.CoreService;
26import org.onosproject.net.DeviceId;
Gustavo Silva3b5a5512022-08-29 16:18:57 -030027import org.onosproject.net.flow.FlowEntry;
28import org.onosproject.net.flow.FlowRule;
29import org.onosproject.net.flow.FlowRuleService;
30import org.onosproject.net.flow.TrafficTreatment;
31import org.onosproject.net.flow.instructions.Instructions;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000032import org.onosproject.net.meter.Band;
33import org.onosproject.net.meter.DefaultBand;
34import org.onosproject.net.meter.DefaultMeterRequest;
35import org.onosproject.net.meter.Meter;
36import org.onosproject.net.meter.MeterContext;
37import org.onosproject.net.meter.MeterEvent;
38import org.onosproject.net.meter.MeterFailReason;
39import org.onosproject.net.meter.MeterId;
40import org.onosproject.net.meter.MeterKey;
41import org.onosproject.net.meter.MeterListener;
42import org.onosproject.net.meter.MeterRequest;
43import org.onosproject.net.meter.MeterService;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070044import org.onosproject.net.meter.MeterState;
Jonathan Hart4f178fa2020-02-03 10:46:01 -080045import org.onosproject.store.serializers.KryoNamespaces;
Jonathan Hart4f178fa2020-02-03 10:46:01 -080046import org.onosproject.store.service.Serializer;
47import org.onosproject.store.service.StorageService;
Gustavo Silva29fb20e2022-05-26 09:59:54 -030048import org.opencord.olt.MeterData;
49import org.opencord.olt.OltDeviceServiceInterface;
50import org.opencord.olt.OltMeterServiceInterface;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000051import org.opencord.sadis.BandwidthProfileInformation;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070052import org.opencord.sadis.BaseInformationService;
53import org.opencord.sadis.SadisService;
54import org.opencord.sadis.SubscriberAndDeviceInformation;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000055import org.osgi.service.component.ComponentContext;
56import org.osgi.service.component.annotations.Activate;
57import org.osgi.service.component.annotations.Component;
58import org.osgi.service.component.annotations.Deactivate;
59import org.osgi.service.component.annotations.Modified;
60import org.osgi.service.component.annotations.Reference;
61import org.osgi.service.component.annotations.ReferenceCardinality;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070062import org.osgi.service.component.annotations.ReferencePolicy;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000063import org.slf4j.Logger;
64
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070065import java.util.ArrayList;
Gustavo Silva3b5a5512022-08-29 16:18:57 -030066import java.util.Collection;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070067import java.util.Dictionary;
68import java.util.HashMap;
69import java.util.Iterator;
70import java.util.LinkedList;
71import java.util.List;
72import java.util.Map;
73import java.util.Properties;
74import java.util.concurrent.CompletableFuture;
75import java.util.concurrent.ExecutorService;
76import java.util.concurrent.Executors;
77import java.util.concurrent.atomic.AtomicBoolean;
78import java.util.concurrent.atomic.AtomicInteger;
79import java.util.concurrent.locks.Lock;
80import java.util.concurrent.locks.ReentrantReadWriteLock;
Gustavo Silva3b5a5512022-08-29 16:18:57 -030081import java.util.stream.StreamSupport;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000082
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070083import static com.google.common.base.Strings.isNullOrEmpty;
84import static org.onlab.util.Tools.get;
85import static org.onlab.util.Tools.groupedThreads;
86import static org.opencord.olt.impl.OsgiPropertyConstants.*;
87import static org.slf4j.LoggerFactory.getLogger;
88
Andrea Campanellacbbb7952019-11-25 06:38:41 +000089@Component(immediate = true, property = {
90 DELETE_METERS + ":Boolean=" + DELETE_METERS_DEFAULT,
Andrea Campanella7e1eb712021-09-22 14:27:35 +020091 ZERO_REFERENCE_METER_COUNT + ":Integer=" + ZERO_REFERENCE_METER_COUNT_DEFAULT,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070092})
93public class OltMeterService implements OltMeterServiceInterface {
Andrea Campanellacbbb7952019-11-25 06:38:41 +000094
95 @Reference(cardinality = ReferenceCardinality.MANDATORY)
96 protected CoreService coreService;
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070099 protected ComponentConfigService cfgService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000100
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800101 @Reference(cardinality = ReferenceCardinality.MANDATORY)
102 protected StorageService storageService;
103
Gustavo Silva3b5a5512022-08-29 16:18:57 -0300104 @Reference(cardinality = ReferenceCardinality.MANDATORY)
105 protected FlowRuleService flowRuleService;
106
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700107 @Reference(cardinality = ReferenceCardinality.OPTIONAL,
108 bind = "bindSadisService",
109 unbind = "unbindSadisService",
110 policy = ReferencePolicy.DYNAMIC)
111 protected volatile SadisService sadisService;
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200112
113 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700114 protected MeterService meterService;
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200115
116 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700117 protected OltDeviceServiceInterface oltDeviceService;
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200118
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700119 private final Logger log = getLogger(getClass());
120 protected BaseInformationService<BandwidthProfileInformation> bpService;
121 private ApplicationId appId;
122 private static final String APP_NAME = "org.opencord.olt";
123 private final ReentrantReadWriteLock programmedMeterLock = new ReentrantReadWriteLock();
124 private final Lock programmedMeterWriteLock = programmedMeterLock.writeLock();
125 private final Lock programmedMeterReadLock = programmedMeterLock.readLock();
126
127 /**
128 * Programmed Meters status map.
129 * Keeps track of which meter is programmed on which device for which BandwidthProfile.
130 * The String key is the BandwidthProfile
131 */
132 protected Map<DeviceId, Map<String, MeterData>> programmedMeters;
133
134 private final MeterListener meterListener = new InternalMeterListener();
135 protected ExecutorService pendingRemovalMetersExecutor =
136 Executors.newFixedThreadPool(5, groupedThreads("onos/olt",
137 "pending-removal-meters-%d", log));
138
139 /**
140 * Map that contains a list of meters that needs to be removed.
141 * We wait to get 3 METER_REFERENCE_COUNT_ZERO events before removing the meter
142 * so that we're sure no flow is referencing it.
143 */
144 protected Map<DeviceId, Map<MeterKey, AtomicInteger>> pendingRemoveMeters;
145
146 /**
147 * Number of consecutive meter events with empty reference count
148 * after which a meter gets removed from the device.
149 */
150 protected int zeroReferenceMeterCount = 3;
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200151
Saurav Dasf62cea82020-08-26 17:43:04 -0700152 /**
153 * Delete meters when reference count drops to zero.
154 */
155 protected boolean deleteMeters = DELETE_METERS_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000156
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000157 @Activate
158 public void activate(ComponentContext context) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000159 appId = coreService.registerApplication(APP_NAME);
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800160 modified(context);
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800161 KryoNamespace serializer = KryoNamespace.newBuilder()
162 .register(KryoNamespaces.API)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700163 .register(List.class)
164 .register(MeterData.class)
165 .register(MeterState.class)
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800166 .register(MeterKey.class)
167 .build();
168
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700169 programmedMeters = storageService.<DeviceId, Map<String, MeterData>>consistentMapBuilder()
170 .withName("volt-programmed-meters")
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000171 .withSerializer(Serializer.using(serializer))
172 .withApplicationId(appId)
173 .build().asJavaMap();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700174
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000175 pendingRemoveMeters = storageService.<DeviceId, Map<MeterKey, AtomicInteger>>consistentMapBuilder()
176 .withName("volt-pending-remove-meters")
177 .withSerializer(Serializer.using(serializer))
178 .withApplicationId(appId)
179 .build().asJavaMap();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000180
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700181 cfgService.registerProperties(getClass());
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000182
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700183 meterService.addListener(meterListener);
184
185 log.info("Started");
186 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000187
188 @Modified
189 public void modified(ComponentContext context) {
190 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
191
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700192 Boolean d = Tools.isPropertyEnabled(properties, DELETE_METERS);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000193 if (d != null) {
194 deleteMeters = d;
195 }
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200196
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700197 String zeroCount = get(properties, ZERO_REFERENCE_METER_COUNT);
198 int oldSubscriberProcessingThreads = zeroReferenceMeterCount;
199 zeroReferenceMeterCount = isNullOrEmpty(zeroCount) ?
200 oldSubscriberProcessingThreads : Integer.parseInt(zeroCount.trim());
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200201
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700202 log.info("Modified. Values = deleteMeters: {}, zeroReferenceMeterCount: {}",
203 deleteMeters, zeroReferenceMeterCount);
204 }
205
206 @Deactivate
207 public void deactivate(ComponentContext context) {
208 cfgService.unregisterProperties(getClass(), false);
209 meterService.removeListener(meterListener);
210 log.info("Stopped");
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000211 }
212
213 @Override
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700214 public Map<DeviceId, Map<String, MeterData>> getProgrammedMeters() {
215 try {
216 programmedMeterReadLock.lock();
217 return ImmutableMap.copyOf(programmedMeters);
218 } finally {
219 programmedMeterReadLock.unlock();
220 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000221 }
222
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700223 /**
224 * Will create a meter if needed and return true once available.
225 *
226 * @param deviceId DeviceId
227 * @param bandwidthProfile Bandwidth Profile Id
228 * @return true
229 */
230 @Override
231 public synchronized boolean createMeter(DeviceId deviceId, String bandwidthProfile) {
232
233 // NOTE it is possible that hasMeterByBandwidthProfile returns false has the meter is in PENDING_ADD
234 // then a different thread changes the meter to ADDED
235 // and thus hasPendingMeterByBandwidthProfile return false as well and we install the meter a second time
236 // this causes an inconsistency between the existing meter and meterId stored in the map
237
238 if (!hasMeterByBandwidthProfile(deviceId, bandwidthProfile)) {
239 // NOTE this is at trace level as it's constantly called by the queue processor
240 if (log.isTraceEnabled()) {
241 log.trace("Missing meter for BandwidthProfile {} on device {}", bandwidthProfile, deviceId);
242 }
243
244 if (!hasPendingMeterByBandwidthProfile(deviceId, bandwidthProfile)) {
245 createMeterForBp(deviceId, bandwidthProfile);
246 }
247 if (log.isTraceEnabled()) {
248 log.trace("Meter is not yet available for {} on device {}",
249 bandwidthProfile, deviceId);
250 }
251 return false;
252 }
253 log.debug("Meter found for BandwidthProfile {} on device {}", bandwidthProfile, deviceId);
254 return true;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000255 }
256
257 @Override
Matteo Scandolo88df8ae2021-11-23 13:12:29 -0800258 public boolean createMeters(DeviceId deviceId, SubscriberAndDeviceInformation si, String multicastServiceName) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700259 // Each UniTagInformation has up to 4 meters,
260 // check and/or create all of them
261 AtomicBoolean waitingOnMeter = new AtomicBoolean();
262 waitingOnMeter.set(false);
263 Map<String, List<String>> pendingMeters = new HashMap<>();
264 si.uniTagList().forEach(uniTagInfo -> {
265 String serviceName = uniTagInfo.getServiceName();
Matteo Scandolo88df8ae2021-11-23 13:12:29 -0800266
267 if (multicastServiceName.equals(uniTagInfo.getServiceName())) {
268 log.debug("This is the multicast service ({}) for subscriber {} on {}, " +
269 "meters are not needed",
270 uniTagInfo.getServiceName(), si.id(), deviceId);
271 return;
272 }
273
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700274 pendingMeters.put(serviceName, new LinkedList<>());
275 String usBp = uniTagInfo.getUpstreamBandwidthProfile();
276 String dsBp = uniTagInfo.getDownstreamBandwidthProfile();
277 String oltUBp = uniTagInfo.getDownstreamOltBandwidthProfile();
278 String oltDsBp = uniTagInfo.getUpstreamOltBandwidthProfile();
279 if (!createMeter(deviceId, usBp)) {
280 pendingMeters.get(serviceName).add(usBp);
281 waitingOnMeter.set(true);
282 }
283 if (!createMeter(deviceId, dsBp)) {
284 pendingMeters.get(serviceName).add(usBp);
285 waitingOnMeter.set(true);
286 }
287 if (!createMeter(deviceId, oltUBp)) {
288 pendingMeters.get(serviceName).add(usBp);
289 waitingOnMeter.set(true);
290 }
291 if (!createMeter(deviceId, oltDsBp)) {
292 pendingMeters.get(serviceName).add(usBp);
293 waitingOnMeter.set(true);
294 }
295 });
296 if (waitingOnMeter.get()) {
297 if (log.isTraceEnabled()) {
298 log.trace("Meters {} on device {} are not " +
299 "installed yet (requested by subscriber {})",
300 pendingMeters, deviceId, si.id());
301 }
302 return false;
yasin saplib4b8ee12021-06-13 18:25:20 +0000303 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700304 return true;
305 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000306
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700307 /**
308 * Returns true if a meter is present in the programmed meters map, only if status is ADDED.
309 *
310 * @param deviceId the DeviceId on which to look for the meter
311 * @param bandwidthProfile the Bandwidth profile associated with this meter
312 * @return true if the meter is found
313 */
314 public boolean hasMeterByBandwidthProfile(DeviceId deviceId, String bandwidthProfile) {
315 try {
316 programmedMeterReadLock.lock();
317 Map<String, MeterData> metersOnDevice = programmedMeters.get(deviceId);
318 if (metersOnDevice == null || metersOnDevice.isEmpty()) {
319 return false;
320 }
321 if (log.isTraceEnabled()) {
322 log.trace("added metersOnDevice {}: {}", deviceId, metersOnDevice);
323 }
324 return metersOnDevice.get(bandwidthProfile) != null &&
325 metersOnDevice.get(bandwidthProfile).getMeterStatus().equals(MeterState.ADDED);
326 } finally {
327 programmedMeterReadLock.unlock();
328 }
329 }
330
331 public boolean hasPendingMeterByBandwidthProfile(DeviceId deviceId, String bandwidthProfile) {
332 try {
333 programmedMeterReadLock.lock();
334 Map<String, MeterData> metersOnDevice = programmedMeters.get(deviceId);
335 if (metersOnDevice == null || metersOnDevice.isEmpty()) {
336 return false;
337 }
338 if (log.isTraceEnabled()) {
339 log.trace("pending metersOnDevice {}: {}", deviceId, metersOnDevice);
340 }
341 // NOTE that we check in order if the meter was ADDED and if it wasn't we check for PENDING_ADD
342 // it is possible that a different thread move the meter state from PENDING_ADD
343 // to ADDED between these two checks
344 // to avoid creating the meter twice we return true event if the meter is already added
345 return metersOnDevice.get(bandwidthProfile) != null && (
346 metersOnDevice.get(bandwidthProfile).getMeterStatus().equals(MeterState.ADDED) ||
347 metersOnDevice.get(bandwidthProfile).getMeterStatus().equals(MeterState.PENDING_ADD)
348 );
349
350 } finally {
351 programmedMeterReadLock.unlock();
352 }
353 }
354
355 public MeterId getMeterIdForBandwidthProfile(DeviceId deviceId, String bandwidthProfile) {
356 try {
357 programmedMeterReadLock.lock();
358 Map<String, MeterData> metersOnDevice = programmedMeters.get(deviceId);
359 if (metersOnDevice == null || metersOnDevice.isEmpty()) {
360 return null;
361 }
362 MeterData meterData = metersOnDevice.get(bandwidthProfile);
363 if (meterData == null || meterData.getMeterStatus() != MeterState.ADDED) {
364 return null;
365 }
366 if (log.isTraceEnabled()) {
367 log.debug("Found meter {} on device {} for bandwidth profile {}",
368 meterData.getMeterId(), deviceId, bandwidthProfile);
369 }
370 return meterData.getMeterId();
371 } finally {
372 programmedMeterReadLock.unlock();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000373 }
374 }
375
376 @Override
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700377 public void purgeDeviceMeters(DeviceId deviceId) {
378 log.debug("Purging meters on device {}", deviceId);
379 meterService.purgeMeters(deviceId);
380
381 // after we purge the meters we also need to clear the map
382 try {
383 programmedMeterWriteLock.lock();
384 programmedMeters.remove(deviceId);
385 } finally {
386 programmedMeterWriteLock.unlock();
387 }
388
389 // and clear the event count
390 // NOTE do we need a lock?
391 pendingRemoveMeters.remove(deviceId);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000392 }
393
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700394 /**
395 * Creates of a meter for a given Bandwidth Profile on a given device.
396 *
397 * @param deviceId the DeviceId
398 * @param bandwidthProfile the BandwidthProfile ID
399 */
400 public void createMeterForBp(DeviceId deviceId, String bandwidthProfile) {
401 // adding meter in pending state to the programmedMeter map
402 try {
403 programmedMeterWriteLock.lock();
404 programmedMeters.compute(deviceId, (d, deviceMeters) -> {
405
406 if (deviceMeters == null) {
407 deviceMeters = new HashMap<>();
408 }
409 // NOTE that this method is only called after verifying a
410 // meter for this BP does not already exist
411 MeterData meterData = new MeterData(
412 null,
413 MeterState.PENDING_ADD,
414 bandwidthProfile
415 );
416 deviceMeters.put(bandwidthProfile, meterData);
417
418 return deviceMeters;
419 });
420 } finally {
421 programmedMeterWriteLock.unlock();
422 }
423
424 BandwidthProfileInformation bpInfo = getBandwidthProfileInformation(bandwidthProfile);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000425 if (bpInfo == null) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700426 log.error("BandwidthProfile {} information not found in sadis", bandwidthProfile);
427 return;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000428 }
429
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700430 log.info("Creating meter for BandwidthProfile {} on device {}", bpInfo.id(), deviceId);
431
432 if (log.isTraceEnabled()) {
433 log.trace("BandwidthProfile: {}", bpInfo);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000434 }
Andrea Campanella672485f2022-03-17 11:50:56 +0100435 try {
436 List<Band> meterBands = createMeterBands(bpInfo);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000437
Andrea Campanella672485f2022-03-17 11:50:56 +0100438 log.info("Meter bands {} for bwp {}", meterBands, bpInfo);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000439
Andrea Campanella672485f2022-03-17 11:50:56 +0100440 CompletableFuture<Object> meterFuture = new CompletableFuture<>();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700441
Andrea Campanella672485f2022-03-17 11:50:56 +0100442 MeterRequest meterRequest = DefaultMeterRequest.builder()
443 .withBands(meterBands)
444 .withUnit(Meter.Unit.KB_PER_SEC)
445 .withContext(new MeterContext() {
446 @Override
447 public void onSuccess(MeterRequest op) {
448 log.info("Meter for BandwidthProfile {} is installed on the device {}",
449 bandwidthProfile, deviceId);
450 meterFuture.complete(null);
451 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000452
Andrea Campanella672485f2022-03-17 11:50:56 +0100453 @Override
454 public void onError(MeterRequest op, MeterFailReason reason) {
455 log.error("Failed installing meter on {} for {}",
456 deviceId, bandwidthProfile);
457 meterFuture.complete(reason);
458 }
459 })
460 .forDevice(deviceId)
461 .fromApp(appId)
462 .burst()
463 .add();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000464
Andrea Campanella672485f2022-03-17 11:50:56 +0100465 // creating the meter
466 Meter meter = meterService.submit(meterRequest);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000467
Andrea Campanella672485f2022-03-17 11:50:56 +0100468 // wait for the meter to be completed
469 meterFuture.thenAccept(error -> {
470 if (error != null) {
471 log.error("Cannot create meter, TODO address me");
472 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700473
Andrea Campanella672485f2022-03-17 11:50:56 +0100474 // then update the map with the MeterId
475 try {
476 programmedMeterWriteLock.lock();
477 programmedMeters.compute(deviceId, (d, entry) -> {
478 if (entry != null) {
479 entry.compute(bandwidthProfile, (bp, meterData) -> {
480 if (meterData != null) {
481 meterData.setMeterCellId(meter.meterCellId());
482 meterData.setMeterStatus(MeterState.ADDED);
483 }
484 return meterData;
485 });
486 }
487 return entry;
488 });
489 } finally {
490 programmedMeterWriteLock.unlock();
491 }
492 });
493 } catch (Exception e) {
494 log.error("", e);
495 }
Andrea Campanella600d2e22020-06-22 11:00:31 +0200496 }
497
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000498 private List<Band> createMeterBands(BandwidthProfileInformation bpInfo) {
499 List<Band> meterBands = new ArrayList<>();
500
Gamze Abakaf46ab432021-03-03 10:51:17 +0000501 // add cir
502 if (bpInfo.committedInformationRate() != 0) {
Andrea Campanella672485f2022-03-17 11:50:56 +0100503 meterBands.add(createMeterBand(bpInfo.committedInformationRate(),
504 bpInfo.committedBurstSize(), Band.Type.DROP, null));
Gamze Abakaf46ab432021-03-03 10:51:17 +0000505 }
506
507 // check if both air and gir are set together in sadis
508 // if they are, set air to 0
509 if (bpInfo.assuredInformationRate() != 0 && bpInfo.guaranteedInformationRate() != 0) {
510 bpInfo.setAssuredInformationRate(0);
511 }
512
513 // add pir
514 long pir = bpInfo.peakInformationRate() != 0 ? bpInfo.peakInformationRate() : (bpInfo.exceededInformationRate()
515 + bpInfo.committedInformationRate() + bpInfo.guaranteedInformationRate()
516 + bpInfo.assuredInformationRate());
517
518 Long pbs = bpInfo.peakBurstSize() != null ? bpInfo.peakBurstSize() :
519 (bpInfo.exceededBurstSize() != null ? bpInfo.exceededBurstSize() : 0) +
520 (bpInfo.committedBurstSize() != null ? bpInfo.committedBurstSize() : 0);
521
Andrea Campanella672485f2022-03-17 11:50:56 +0100522 meterBands.add(createMeterBand(pir, pbs, Band.Type.REMARK, (short) 1));
Gamze Abakaf46ab432021-03-03 10:51:17 +0000523
524 // add gir
Andrea Campanella672485f2022-03-17 11:50:56 +0100525 //We can use DROP here because it GIr will never be equals to cir so rate will always be different.
Gamze Abakaf46ab432021-03-03 10:51:17 +0000526 if (bpInfo.guaranteedInformationRate() != 0) {
Andrea Campanella672485f2022-03-17 11:50:56 +0100527 meterBands.add(createMeterBand(bpInfo.guaranteedInformationRate(), 0L, Band.Type.DROP, null));
Gamze Abakaf46ab432021-03-03 10:51:17 +0000528 }
529
530 // add air
531 // air is used in place of gir only if gir is
532 // not present and air is not 0, see line 330.
533 // Included for backwards compatibility, will be removed in VOLTHA 2.9.
Andrea Campanella672485f2022-03-17 11:50:56 +0100534 // Using Band.Type.NONE is ok because this will be removed.
Gamze Abakaf46ab432021-03-03 10:51:17 +0000535 if (bpInfo.assuredInformationRate() != 0) {
Andrea Campanella672485f2022-03-17 11:50:56 +0100536 meterBands.add(createMeterBand(bpInfo.assuredInformationRate(), 0L, Band.Type.DROP, null));
Gamze Abakaf46ab432021-03-03 10:51:17 +0000537 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000538
539 return meterBands;
540 }
541
Andrea Campanella672485f2022-03-17 11:50:56 +0100542 private Band createMeterBand(long rate, Long burst, Band.Type type, Short precedence) {
543 Band.Builder bandBuilder = DefaultBand.builder()
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000544 .withRate(rate) //already Kbps
545 .burstSize(burst) // already Kbits
Andrea Campanella672485f2022-03-17 11:50:56 +0100546 .ofType(type); // no matter
547 if (precedence != null) {
548 bandBuilder.dropPrecedence(precedence);
549 }
550
551 return bandBuilder.build();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000552 }
553
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700554 private BandwidthProfileInformation getBandwidthProfileInformation(String bandwidthProfile) {
555 if (!checkSadisRunning()) {
556 return null;
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200557 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700558 if (bandwidthProfile == null) {
559 return null;
560 }
561 return bpService.get(bandwidthProfile);
562 }
563
564 private boolean checkSadisRunning() {
565 if (bpService == null) {
566 log.warn("Sadis is not running");
567 return false;
568 }
569 return true;
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200570 }
571
Andrea Campanella600d2e22020-06-22 11:00:31 +0200572 private class InternalMeterListener implements MeterListener {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000573 @Override
574 public void event(MeterEvent meterEvent) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700575 pendingRemovalMetersExecutor.execute(() -> {
576
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000577 Meter meter = meterEvent.subject();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700578 if (!appId.equals(meter.appId())) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000579 return;
580 }
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200581
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700582 if (log.isTraceEnabled()) {
583 log.trace("Received meter event {}", meterEvent);
584 }
585 MeterKey key = MeterKey.key(meter.deviceId(), meter.id());
586 if (meterEvent.type().equals(MeterEvent.Type.METER_REFERENCE_COUNT_ZERO)) {
587 if (!oltDeviceService.isLocalLeader(meter.deviceId())) {
588 if (log.isTraceEnabled()) {
589 log.trace("ignoring meter event {} " +
590 "as not leader for {}", meterEvent, meter.deviceId());
591 }
592 return;
593 }
594 log.info("Zero Count Reference event is received for meter {} on {}, " +
595 "incrementing counter",
596 meter.id(), meter.deviceId());
597 incrementMeterCount(meter.deviceId(), key);
598 if (pendingRemoveMeters.get(meter.deviceId())
Gustavo Silva3b5a5512022-08-29 16:18:57 -0300599 .get(key).get() >= zeroReferenceMeterCount) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700600 // only delete the meters if the app is configured to do so
601 if (deleteMeters) {
Gustavo Silva3b5a5512022-08-29 16:18:57 -0300602 // Check if there's any pending flow referencing that meter.
603 if (isUsedByPendingAddFlow(meter)) {
604 log.info("Meter {} is still being referenced by pending flows, avoiding removal.",
605 meter.id());
606 removeMeterCount(meter, key);
607 return;
608 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700609 log.info("Meter {} on device {} is unused, removing it", meter.id(), meter.deviceId());
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200610 deleteMeter(meter.deviceId(), meter.id());
611 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000612 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700613 }
614
615 if (meterEvent.type().equals(MeterEvent.Type.METER_REMOVED)) {
616 removeMeterCount(meter, key);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000617 }
618 });
619 }
620
Gustavo Silva3b5a5512022-08-29 16:18:57 -0300621 private boolean isUsedByPendingAddFlow(Meter meter) {
622 Long meterId = meter.id().id();
623 Iterable<FlowEntry> pendingAddFlows = flowRuleService.getFlowEntriesByState(meter.deviceId(),
624 FlowEntry.FlowEntryState.PENDING_ADD);
625 return StreamSupport.stream(pendingAddFlows.spliterator(), true)
626 .map(FlowRule::treatment)
627 .map(TrafficTreatment::meters)
628 .flatMap(Collection::parallelStream)
629 .map(Instructions.MeterInstruction::meterId)
630 .map(Identifier::id)
631 .anyMatch(meterId::equals);
632 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700633 private void removeMeterCount(Meter meter, MeterKey key) {
634 pendingRemoveMeters.computeIfPresent(meter.deviceId(),
635 (id, meters) -> {
636 if (meters.get(key) == null) {
637 log.info("Meters is not pending " +
638 "{} on {}", key, id);
639 return meters;
640 }
641 meters.remove(key);
642 return meters;
643 });
644 }
645
Andrea Campanella600d2e22020-06-22 11:00:31 +0200646 private void incrementMeterCount(DeviceId deviceId, MeterKey key) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000647 if (key == null) {
648 return;
649 }
Andrea Campanella600d2e22020-06-22 11:00:31 +0200650 pendingRemoveMeters.compute(deviceId,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700651 (id, meters) -> {
652 if (meters == null) {
653 meters = new HashMap<>();
Andrea Campanella600d2e22020-06-22 11:00:31 +0200654
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700655 }
656 if (meters.get(key) == null) {
657 meters.put(key, new AtomicInteger(1));
658 }
659 meters.get(key).addAndGet(1);
660 return meters;
661 });
662 }
663 }
664
665 private void deleteMeter(DeviceId deviceId, MeterId meterId) {
666 Meter meter = meterService.getMeter(deviceId, meterId);
667 if (meter != null) {
668 MeterRequest meterRequest = DefaultMeterRequest.builder()
669 .withBands(meter.bands())
670 .withUnit(meter.unit())
671 .forDevice(deviceId)
672 .fromApp(appId)
673 .burst()
674 .remove();
675
676 meterService.withdraw(meterRequest, meterId);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000677 }
678
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700679 // remove the meter from local caching
680 try {
681 programmedMeterWriteLock.lock();
682 programmedMeters.computeIfPresent(deviceId, (d, deviceMeters) -> {
683 Iterator<Map.Entry<String, MeterData>> iter = deviceMeters.entrySet().iterator();
684 while (iter.hasNext()) {
685 Map.Entry<String, MeterData> entry = iter.next();
686 if (entry.getValue().getMeterId().equals(meterId)) {
687 deviceMeters.remove(entry.getKey());
688 }
689 }
690 return deviceMeters;
691 });
692 } finally {
693 programmedMeterWriteLock.unlock();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000694 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000695 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700696
697 protected void bindSadisService(SadisService service) {
698 this.bpService = service.getBandwidthProfileService();
699 log.info("Sadis service is loaded");
700 }
701
702 protected void unbindSadisService(SadisService service) {
703 this.bpService = null;
704 log.info("Sadis service is unloaded");
705 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000706}