blob: be0444900989f4b020c7100fd814f86ab78f8325 [file] [log] [blame]
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001/*
Matteo Scandoloaa2adde2021-09-13 12:45:32 -07002 * Copyright 2021-present Open Networking Foundation
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;
Jonathan Hart4f178fa2020-02-03 10:46:01 -080020import org.onlab.util.KryoNamespace;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000021import org.onlab.util.Tools;
22import org.onosproject.cfg.ComponentConfigService;
23import org.onosproject.core.ApplicationId;
24import org.onosproject.core.CoreService;
25import org.onosproject.net.DeviceId;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000026import org.onosproject.net.meter.Band;
27import org.onosproject.net.meter.DefaultBand;
28import org.onosproject.net.meter.DefaultMeterRequest;
29import org.onosproject.net.meter.Meter;
30import org.onosproject.net.meter.MeterContext;
31import org.onosproject.net.meter.MeterEvent;
32import org.onosproject.net.meter.MeterFailReason;
33import org.onosproject.net.meter.MeterId;
34import org.onosproject.net.meter.MeterKey;
35import org.onosproject.net.meter.MeterListener;
36import org.onosproject.net.meter.MeterRequest;
37import org.onosproject.net.meter.MeterService;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070038import org.onosproject.net.meter.MeterState;
Jonathan Hart4f178fa2020-02-03 10:46:01 -080039import org.onosproject.store.serializers.KryoNamespaces;
Jonathan Hart4f178fa2020-02-03 10:46:01 -080040import org.onosproject.store.service.Serializer;
41import org.onosproject.store.service.StorageService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000042import org.opencord.sadis.BandwidthProfileInformation;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070043import org.opencord.sadis.BaseInformationService;
44import org.opencord.sadis.SadisService;
45import org.opencord.sadis.SubscriberAndDeviceInformation;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000046import org.osgi.service.component.ComponentContext;
47import org.osgi.service.component.annotations.Activate;
48import org.osgi.service.component.annotations.Component;
49import org.osgi.service.component.annotations.Deactivate;
50import org.osgi.service.component.annotations.Modified;
51import org.osgi.service.component.annotations.Reference;
52import org.osgi.service.component.annotations.ReferenceCardinality;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070053import org.osgi.service.component.annotations.ReferencePolicy;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000054import org.slf4j.Logger;
55
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070056import java.util.ArrayList;
57import java.util.Dictionary;
58import java.util.HashMap;
59import java.util.Iterator;
60import java.util.LinkedList;
61import java.util.List;
62import java.util.Map;
63import java.util.Properties;
64import java.util.concurrent.CompletableFuture;
65import java.util.concurrent.ExecutorService;
66import java.util.concurrent.Executors;
67import java.util.concurrent.atomic.AtomicBoolean;
68import java.util.concurrent.atomic.AtomicInteger;
69import java.util.concurrent.locks.Lock;
70import java.util.concurrent.locks.ReentrantReadWriteLock;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000071
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070072import static com.google.common.base.Strings.isNullOrEmpty;
73import static org.onlab.util.Tools.get;
74import static org.onlab.util.Tools.groupedThreads;
75import static org.opencord.olt.impl.OsgiPropertyConstants.*;
76import static org.slf4j.LoggerFactory.getLogger;
77
Andrea Campanellacbbb7952019-11-25 06:38:41 +000078@Component(immediate = true, property = {
79 DELETE_METERS + ":Boolean=" + DELETE_METERS_DEFAULT,
Andrea Campanella7e1eb712021-09-22 14:27:35 +020080 ZERO_REFERENCE_METER_COUNT + ":Integer=" + ZERO_REFERENCE_METER_COUNT_DEFAULT,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070081})
82public class OltMeterService implements OltMeterServiceInterface {
Andrea Campanellacbbb7952019-11-25 06:38:41 +000083
84 @Reference(cardinality = ReferenceCardinality.MANDATORY)
85 protected CoreService coreService;
86
87 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070088 protected ComponentConfigService cfgService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000089
Jonathan Hart4f178fa2020-02-03 10:46:01 -080090 @Reference(cardinality = ReferenceCardinality.MANDATORY)
91 protected StorageService storageService;
92
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070093 @Reference(cardinality = ReferenceCardinality.OPTIONAL,
94 bind = "bindSadisService",
95 unbind = "unbindSadisService",
96 policy = ReferencePolicy.DYNAMIC)
97 protected volatile SadisService sadisService;
Andrea Campanella7e1eb712021-09-22 14:27:35 +020098
99 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700100 protected MeterService meterService;
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700103 protected OltDeviceServiceInterface oltDeviceService;
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200104
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700105 private final Logger log = getLogger(getClass());
106 protected BaseInformationService<BandwidthProfileInformation> bpService;
107 private ApplicationId appId;
108 private static final String APP_NAME = "org.opencord.olt";
109 private final ReentrantReadWriteLock programmedMeterLock = new ReentrantReadWriteLock();
110 private final Lock programmedMeterWriteLock = programmedMeterLock.writeLock();
111 private final Lock programmedMeterReadLock = programmedMeterLock.readLock();
112
113 /**
114 * Programmed Meters status map.
115 * Keeps track of which meter is programmed on which device for which BandwidthProfile.
116 * The String key is the BandwidthProfile
117 */
118 protected Map<DeviceId, Map<String, MeterData>> programmedMeters;
119
120 private final MeterListener meterListener = new InternalMeterListener();
121 protected ExecutorService pendingRemovalMetersExecutor =
122 Executors.newFixedThreadPool(5, groupedThreads("onos/olt",
123 "pending-removal-meters-%d", log));
124
125 /**
126 * Map that contains a list of meters that needs to be removed.
127 * We wait to get 3 METER_REFERENCE_COUNT_ZERO events before removing the meter
128 * so that we're sure no flow is referencing it.
129 */
130 protected Map<DeviceId, Map<MeterKey, AtomicInteger>> pendingRemoveMeters;
131
132 /**
133 * Number of consecutive meter events with empty reference count
134 * after which a meter gets removed from the device.
135 */
136 protected int zeroReferenceMeterCount = 3;
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200137
Saurav Dasf62cea82020-08-26 17:43:04 -0700138 /**
139 * Delete meters when reference count drops to zero.
140 */
141 protected boolean deleteMeters = DELETE_METERS_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000142
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000143 @Activate
144 public void activate(ComponentContext context) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000145 appId = coreService.registerApplication(APP_NAME);
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800146 modified(context);
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800147 KryoNamespace serializer = KryoNamespace.newBuilder()
148 .register(KryoNamespaces.API)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700149 .register(List.class)
150 .register(MeterData.class)
151 .register(MeterState.class)
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800152 .register(MeterKey.class)
153 .build();
154
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700155 programmedMeters = storageService.<DeviceId, Map<String, MeterData>>consistentMapBuilder()
156 .withName("volt-programmed-meters")
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000157 .withSerializer(Serializer.using(serializer))
158 .withApplicationId(appId)
159 .build().asJavaMap();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700160
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000161 pendingRemoveMeters = storageService.<DeviceId, Map<MeterKey, AtomicInteger>>consistentMapBuilder()
162 .withName("volt-pending-remove-meters")
163 .withSerializer(Serializer.using(serializer))
164 .withApplicationId(appId)
165 .build().asJavaMap();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000166
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700167 cfgService.registerProperties(getClass());
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000168
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700169 meterService.addListener(meterListener);
170
171 log.info("Started");
172 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000173
174 @Modified
175 public void modified(ComponentContext context) {
176 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
177
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700178 Boolean d = Tools.isPropertyEnabled(properties, DELETE_METERS);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000179 if (d != null) {
180 deleteMeters = d;
181 }
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200182
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700183 String zeroCount = get(properties, ZERO_REFERENCE_METER_COUNT);
184 int oldSubscriberProcessingThreads = zeroReferenceMeterCount;
185 zeroReferenceMeterCount = isNullOrEmpty(zeroCount) ?
186 oldSubscriberProcessingThreads : Integer.parseInt(zeroCount.trim());
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200187
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700188 log.info("Modified. Values = deleteMeters: {}, zeroReferenceMeterCount: {}",
189 deleteMeters, zeroReferenceMeterCount);
190 }
191
192 @Deactivate
193 public void deactivate(ComponentContext context) {
194 cfgService.unregisterProperties(getClass(), false);
195 meterService.removeListener(meterListener);
196 log.info("Stopped");
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000197 }
198
199 @Override
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700200 public Map<DeviceId, Map<String, MeterData>> getProgrammedMeters() {
201 try {
202 programmedMeterReadLock.lock();
203 return ImmutableMap.copyOf(programmedMeters);
204 } finally {
205 programmedMeterReadLock.unlock();
206 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000207 }
208
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700209 /**
210 * Will create a meter if needed and return true once available.
211 *
212 * @param deviceId DeviceId
213 * @param bandwidthProfile Bandwidth Profile Id
214 * @return true
215 */
216 @Override
217 public synchronized boolean createMeter(DeviceId deviceId, String bandwidthProfile) {
218
219 // NOTE it is possible that hasMeterByBandwidthProfile returns false has the meter is in PENDING_ADD
220 // then a different thread changes the meter to ADDED
221 // and thus hasPendingMeterByBandwidthProfile return false as well and we install the meter a second time
222 // this causes an inconsistency between the existing meter and meterId stored in the map
223
224 if (!hasMeterByBandwidthProfile(deviceId, bandwidthProfile)) {
225 // NOTE this is at trace level as it's constantly called by the queue processor
226 if (log.isTraceEnabled()) {
227 log.trace("Missing meter for BandwidthProfile {} on device {}", bandwidthProfile, deviceId);
228 }
229
230 if (!hasPendingMeterByBandwidthProfile(deviceId, bandwidthProfile)) {
231 createMeterForBp(deviceId, bandwidthProfile);
232 }
233 if (log.isTraceEnabled()) {
234 log.trace("Meter is not yet available for {} on device {}",
235 bandwidthProfile, deviceId);
236 }
237 return false;
238 }
239 log.debug("Meter found for BandwidthProfile {} on device {}", bandwidthProfile, deviceId);
240 return true;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000241 }
242
243 @Override
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700244 public boolean createMeters(DeviceId deviceId, SubscriberAndDeviceInformation si) {
245 // Each UniTagInformation has up to 4 meters,
246 // check and/or create all of them
247 AtomicBoolean waitingOnMeter = new AtomicBoolean();
248 waitingOnMeter.set(false);
249 Map<String, List<String>> pendingMeters = new HashMap<>();
250 si.uniTagList().forEach(uniTagInfo -> {
251 String serviceName = uniTagInfo.getServiceName();
252 pendingMeters.put(serviceName, new LinkedList<>());
253 String usBp = uniTagInfo.getUpstreamBandwidthProfile();
254 String dsBp = uniTagInfo.getDownstreamBandwidthProfile();
255 String oltUBp = uniTagInfo.getDownstreamOltBandwidthProfile();
256 String oltDsBp = uniTagInfo.getUpstreamOltBandwidthProfile();
257 if (!createMeter(deviceId, usBp)) {
258 pendingMeters.get(serviceName).add(usBp);
259 waitingOnMeter.set(true);
260 }
261 if (!createMeter(deviceId, dsBp)) {
262 pendingMeters.get(serviceName).add(usBp);
263 waitingOnMeter.set(true);
264 }
265 if (!createMeter(deviceId, oltUBp)) {
266 pendingMeters.get(serviceName).add(usBp);
267 waitingOnMeter.set(true);
268 }
269 if (!createMeter(deviceId, oltDsBp)) {
270 pendingMeters.get(serviceName).add(usBp);
271 waitingOnMeter.set(true);
272 }
273 });
274 if (waitingOnMeter.get()) {
275 if (log.isTraceEnabled()) {
276 log.trace("Meters {} on device {} are not " +
277 "installed yet (requested by subscriber {})",
278 pendingMeters, deviceId, si.id());
279 }
280 return false;
yasin saplib4b8ee12021-06-13 18:25:20 +0000281 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700282 return true;
283 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000284
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700285 /**
286 * Returns true if a meter is present in the programmed meters map, only if status is ADDED.
287 *
288 * @param deviceId the DeviceId on which to look for the meter
289 * @param bandwidthProfile the Bandwidth profile associated with this meter
290 * @return true if the meter is found
291 */
292 public boolean hasMeterByBandwidthProfile(DeviceId deviceId, String bandwidthProfile) {
293 try {
294 programmedMeterReadLock.lock();
295 Map<String, MeterData> metersOnDevice = programmedMeters.get(deviceId);
296 if (metersOnDevice == null || metersOnDevice.isEmpty()) {
297 return false;
298 }
299 if (log.isTraceEnabled()) {
300 log.trace("added metersOnDevice {}: {}", deviceId, metersOnDevice);
301 }
302 return metersOnDevice.get(bandwidthProfile) != null &&
303 metersOnDevice.get(bandwidthProfile).getMeterStatus().equals(MeterState.ADDED);
304 } finally {
305 programmedMeterReadLock.unlock();
306 }
307 }
308
309 public boolean hasPendingMeterByBandwidthProfile(DeviceId deviceId, String bandwidthProfile) {
310 try {
311 programmedMeterReadLock.lock();
312 Map<String, MeterData> metersOnDevice = programmedMeters.get(deviceId);
313 if (metersOnDevice == null || metersOnDevice.isEmpty()) {
314 return false;
315 }
316 if (log.isTraceEnabled()) {
317 log.trace("pending metersOnDevice {}: {}", deviceId, metersOnDevice);
318 }
319 // NOTE that we check in order if the meter was ADDED and if it wasn't we check for PENDING_ADD
320 // it is possible that a different thread move the meter state from PENDING_ADD
321 // to ADDED between these two checks
322 // to avoid creating the meter twice we return true event if the meter is already added
323 return metersOnDevice.get(bandwidthProfile) != null && (
324 metersOnDevice.get(bandwidthProfile).getMeterStatus().equals(MeterState.ADDED) ||
325 metersOnDevice.get(bandwidthProfile).getMeterStatus().equals(MeterState.PENDING_ADD)
326 );
327
328 } finally {
329 programmedMeterReadLock.unlock();
330 }
331 }
332
333 public MeterId getMeterIdForBandwidthProfile(DeviceId deviceId, String bandwidthProfile) {
334 try {
335 programmedMeterReadLock.lock();
336 Map<String, MeterData> metersOnDevice = programmedMeters.get(deviceId);
337 if (metersOnDevice == null || metersOnDevice.isEmpty()) {
338 return null;
339 }
340 MeterData meterData = metersOnDevice.get(bandwidthProfile);
341 if (meterData == null || meterData.getMeterStatus() != MeterState.ADDED) {
342 return null;
343 }
344 if (log.isTraceEnabled()) {
345 log.debug("Found meter {} on device {} for bandwidth profile {}",
346 meterData.getMeterId(), deviceId, bandwidthProfile);
347 }
348 return meterData.getMeterId();
349 } finally {
350 programmedMeterReadLock.unlock();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000351 }
352 }
353
354 @Override
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700355 public void purgeDeviceMeters(DeviceId deviceId) {
356 log.debug("Purging meters on device {}", deviceId);
357 meterService.purgeMeters(deviceId);
358
359 // after we purge the meters we also need to clear the map
360 try {
361 programmedMeterWriteLock.lock();
362 programmedMeters.remove(deviceId);
363 } finally {
364 programmedMeterWriteLock.unlock();
365 }
366
367 // and clear the event count
368 // NOTE do we need a lock?
369 pendingRemoveMeters.remove(deviceId);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000370 }
371
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700372 /**
373 * Creates of a meter for a given Bandwidth Profile on a given device.
374 *
375 * @param deviceId the DeviceId
376 * @param bandwidthProfile the BandwidthProfile ID
377 */
378 public void createMeterForBp(DeviceId deviceId, String bandwidthProfile) {
379 // adding meter in pending state to the programmedMeter map
380 try {
381 programmedMeterWriteLock.lock();
382 programmedMeters.compute(deviceId, (d, deviceMeters) -> {
383
384 if (deviceMeters == null) {
385 deviceMeters = new HashMap<>();
386 }
387 // NOTE that this method is only called after verifying a
388 // meter for this BP does not already exist
389 MeterData meterData = new MeterData(
390 null,
391 MeterState.PENDING_ADD,
392 bandwidthProfile
393 );
394 deviceMeters.put(bandwidthProfile, meterData);
395
396 return deviceMeters;
397 });
398 } finally {
399 programmedMeterWriteLock.unlock();
400 }
401
402 BandwidthProfileInformation bpInfo = getBandwidthProfileInformation(bandwidthProfile);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000403 if (bpInfo == null) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700404 log.error("BandwidthProfile {} information not found in sadis", bandwidthProfile);
405 return;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000406 }
407
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700408 log.info("Creating meter for BandwidthProfile {} on device {}", bpInfo.id(), deviceId);
409
410 if (log.isTraceEnabled()) {
411 log.trace("BandwidthProfile: {}", bpInfo);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000412 }
413
414 List<Band> meterBands = createMeterBands(bpInfo);
415
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700416 CompletableFuture<Object> meterFuture = new CompletableFuture<>();
417
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000418 MeterRequest meterRequest = DefaultMeterRequest.builder()
419 .withBands(meterBands)
420 .withUnit(Meter.Unit.KB_PER_SEC)
421 .withContext(new MeterContext() {
422 @Override
423 public void onSuccess(MeterRequest op) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700424 log.info("Meter for BandwidthProfile {} is installed on the device {}",
425 bandwidthProfile, deviceId);
426 meterFuture.complete(null);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000427 }
428
429 @Override
430 public void onError(MeterRequest op, MeterFailReason reason) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700431 log.error("Failed installing meter on {} for {}",
432 deviceId, bandwidthProfile);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000433 meterFuture.complete(reason);
434 }
435 })
436 .forDevice(deviceId)
437 .fromApp(appId)
438 .burst()
439 .add();
440
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700441 // creating the meter
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000442 Meter meter = meterService.submit(meterRequest);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000443
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700444 // wait for the meter to be completed
445 meterFuture.thenAccept(error -> {
446 if (error != null) {
447 log.error("Cannot create meter, TODO address me");
Andrea Campanellad1e26642020-10-23 12:08:32 +0200448 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700449
450 // then update the map with the MeterId
451 try {
452 programmedMeterWriteLock.lock();
453 programmedMeters.compute(deviceId, (d, entry) -> {
454 if (entry != null) {
455 entry.compute(bandwidthProfile, (bp, meterData) -> {
456 if (meterData != null) {
457 meterData.setMeterCellId(meter.meterCellId());
458 meterData.setMeterStatus(MeterState.ADDED);
459 }
460 return meterData;
461 });
462 }
463 return entry;
464 });
465 } finally {
466 programmedMeterWriteLock.unlock();
467 }
Andrea Campanellad1e26642020-10-23 12:08:32 +0200468 });
Andrea Campanella600d2e22020-06-22 11:00:31 +0200469 }
470
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000471 private List<Band> createMeterBands(BandwidthProfileInformation bpInfo) {
472 List<Band> meterBands = new ArrayList<>();
473
Gamze Abakaf46ab432021-03-03 10:51:17 +0000474 // add cir
475 if (bpInfo.committedInformationRate() != 0) {
476 meterBands.add(createMeterBand(bpInfo.committedInformationRate(), bpInfo.committedBurstSize()));
477 }
478
479 // check if both air and gir are set together in sadis
480 // if they are, set air to 0
481 if (bpInfo.assuredInformationRate() != 0 && bpInfo.guaranteedInformationRate() != 0) {
482 bpInfo.setAssuredInformationRate(0);
483 }
484
485 // add pir
486 long pir = bpInfo.peakInformationRate() != 0 ? bpInfo.peakInformationRate() : (bpInfo.exceededInformationRate()
487 + bpInfo.committedInformationRate() + bpInfo.guaranteedInformationRate()
488 + bpInfo.assuredInformationRate());
489
490 Long pbs = bpInfo.peakBurstSize() != null ? bpInfo.peakBurstSize() :
491 (bpInfo.exceededBurstSize() != null ? bpInfo.exceededBurstSize() : 0) +
492 (bpInfo.committedBurstSize() != null ? bpInfo.committedBurstSize() : 0);
493
494 meterBands.add(createMeterBand(pir, pbs));
495
496 // add gir
497 if (bpInfo.guaranteedInformationRate() != 0) {
498 meterBands.add(createMeterBand(bpInfo.guaranteedInformationRate(), 0L));
499 }
500
501 // add air
502 // air is used in place of gir only if gir is
503 // not present and air is not 0, see line 330.
504 // Included for backwards compatibility, will be removed in VOLTHA 2.9.
505 if (bpInfo.assuredInformationRate() != 0) {
506 meterBands.add(createMeterBand(bpInfo.assuredInformationRate(), 0L));
507 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000508
509 return meterBands;
510 }
511
512 private Band createMeterBand(long rate, Long burst) {
513 return DefaultBand.builder()
514 .withRate(rate) //already Kbps
515 .burstSize(burst) // already Kbits
516 .ofType(Band.Type.DROP) // no matter
517 .build();
518 }
519
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700520 private BandwidthProfileInformation getBandwidthProfileInformation(String bandwidthProfile) {
521 if (!checkSadisRunning()) {
522 return null;
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200523 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700524 if (bandwidthProfile == null) {
525 return null;
526 }
527 return bpService.get(bandwidthProfile);
528 }
529
530 private boolean checkSadisRunning() {
531 if (bpService == null) {
532 log.warn("Sadis is not running");
533 return false;
534 }
535 return true;
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200536 }
537
Andrea Campanella600d2e22020-06-22 11:00:31 +0200538 private class InternalMeterListener implements MeterListener {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000539 @Override
540 public void event(MeterEvent meterEvent) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700541 pendingRemovalMetersExecutor.execute(() -> {
542
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000543 Meter meter = meterEvent.subject();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700544 if (!appId.equals(meter.appId())) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000545 return;
546 }
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200547
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700548 if (log.isTraceEnabled()) {
549 log.trace("Received meter event {}", meterEvent);
550 }
551 MeterKey key = MeterKey.key(meter.deviceId(), meter.id());
552 if (meterEvent.type().equals(MeterEvent.Type.METER_REFERENCE_COUNT_ZERO)) {
553 if (!oltDeviceService.isLocalLeader(meter.deviceId())) {
554 if (log.isTraceEnabled()) {
555 log.trace("ignoring meter event {} " +
556 "as not leader for {}", meterEvent, meter.deviceId());
557 }
558 return;
559 }
560 log.info("Zero Count Reference event is received for meter {} on {}, " +
561 "incrementing counter",
562 meter.id(), meter.deviceId());
563 incrementMeterCount(meter.deviceId(), key);
564 if (pendingRemoveMeters.get(meter.deviceId())
565 .get(key).get() == zeroReferenceMeterCount) {
566 // only delete the meters if the app is configured to do so
567 if (deleteMeters) {
568 log.info("Meter {} on device {} is unused, removing it", meter.id(), meter.deviceId());
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200569 deleteMeter(meter.deviceId(), meter.id());
570 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000571 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700572 }
573
574 if (meterEvent.type().equals(MeterEvent.Type.METER_REMOVED)) {
575 removeMeterCount(meter, key);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000576 }
577 });
578 }
579
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700580 private void removeMeterCount(Meter meter, MeterKey key) {
581 pendingRemoveMeters.computeIfPresent(meter.deviceId(),
582 (id, meters) -> {
583 if (meters.get(key) == null) {
584 log.info("Meters is not pending " +
585 "{} on {}", key, id);
586 return meters;
587 }
588 meters.remove(key);
589 return meters;
590 });
591 }
592
Andrea Campanella600d2e22020-06-22 11:00:31 +0200593 private void incrementMeterCount(DeviceId deviceId, MeterKey key) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000594 if (key == null) {
595 return;
596 }
Andrea Campanella600d2e22020-06-22 11:00:31 +0200597 pendingRemoveMeters.compute(deviceId,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700598 (id, meters) -> {
599 if (meters == null) {
600 meters = new HashMap<>();
Andrea Campanella600d2e22020-06-22 11:00:31 +0200601
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700602 }
603 if (meters.get(key) == null) {
604 meters.put(key, new AtomicInteger(1));
605 }
606 meters.get(key).addAndGet(1);
607 return meters;
608 });
609 }
610 }
611
612 private void deleteMeter(DeviceId deviceId, MeterId meterId) {
613 Meter meter = meterService.getMeter(deviceId, meterId);
614 if (meter != null) {
615 MeterRequest meterRequest = DefaultMeterRequest.builder()
616 .withBands(meter.bands())
617 .withUnit(meter.unit())
618 .forDevice(deviceId)
619 .fromApp(appId)
620 .burst()
621 .remove();
622
623 meterService.withdraw(meterRequest, meterId);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000624 }
625
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700626 // remove the meter from local caching
627 try {
628 programmedMeterWriteLock.lock();
629 programmedMeters.computeIfPresent(deviceId, (d, deviceMeters) -> {
630 Iterator<Map.Entry<String, MeterData>> iter = deviceMeters.entrySet().iterator();
631 while (iter.hasNext()) {
632 Map.Entry<String, MeterData> entry = iter.next();
633 if (entry.getValue().getMeterId().equals(meterId)) {
634 deviceMeters.remove(entry.getKey());
635 }
636 }
637 return deviceMeters;
638 });
639 } finally {
640 programmedMeterWriteLock.unlock();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000641 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000642 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700643
644 protected void bindSadisService(SadisService service) {
645 this.bpService = service.getBandwidthProfileService();
646 log.info("Sadis service is loaded");
647 }
648
649 protected void unbindSadisService(SadisService service) {
650 this.bpService = null;
651 log.info("Sadis service is unloaded");
652 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000653}