blob: 3c7410b98c01a25b0b058a0fc9b7d099d40a6316 [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 Scandolo88df8ae2021-11-23 13:12:29 -0800244 public boolean createMeters(DeviceId deviceId, SubscriberAndDeviceInformation si, String multicastServiceName) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700245 // 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();
Matteo Scandolo88df8ae2021-11-23 13:12:29 -0800252
253 if (multicastServiceName.equals(uniTagInfo.getServiceName())) {
254 log.debug("This is the multicast service ({}) for subscriber {} on {}, " +
255 "meters are not needed",
256 uniTagInfo.getServiceName(), si.id(), deviceId);
257 return;
258 }
259
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700260 pendingMeters.put(serviceName, new LinkedList<>());
261 String usBp = uniTagInfo.getUpstreamBandwidthProfile();
262 String dsBp = uniTagInfo.getDownstreamBandwidthProfile();
263 String oltUBp = uniTagInfo.getDownstreamOltBandwidthProfile();
264 String oltDsBp = uniTagInfo.getUpstreamOltBandwidthProfile();
265 if (!createMeter(deviceId, usBp)) {
266 pendingMeters.get(serviceName).add(usBp);
267 waitingOnMeter.set(true);
268 }
269 if (!createMeter(deviceId, dsBp)) {
270 pendingMeters.get(serviceName).add(usBp);
271 waitingOnMeter.set(true);
272 }
273 if (!createMeter(deviceId, oltUBp)) {
274 pendingMeters.get(serviceName).add(usBp);
275 waitingOnMeter.set(true);
276 }
277 if (!createMeter(deviceId, oltDsBp)) {
278 pendingMeters.get(serviceName).add(usBp);
279 waitingOnMeter.set(true);
280 }
281 });
282 if (waitingOnMeter.get()) {
283 if (log.isTraceEnabled()) {
284 log.trace("Meters {} on device {} are not " +
285 "installed yet (requested by subscriber {})",
286 pendingMeters, deviceId, si.id());
287 }
288 return false;
yasin saplib4b8ee12021-06-13 18:25:20 +0000289 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700290 return true;
291 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000292
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700293 /**
294 * Returns true if a meter is present in the programmed meters map, only if status is ADDED.
295 *
296 * @param deviceId the DeviceId on which to look for the meter
297 * @param bandwidthProfile the Bandwidth profile associated with this meter
298 * @return true if the meter is found
299 */
300 public boolean hasMeterByBandwidthProfile(DeviceId deviceId, String bandwidthProfile) {
301 try {
302 programmedMeterReadLock.lock();
303 Map<String, MeterData> metersOnDevice = programmedMeters.get(deviceId);
304 if (metersOnDevice == null || metersOnDevice.isEmpty()) {
305 return false;
306 }
307 if (log.isTraceEnabled()) {
308 log.trace("added metersOnDevice {}: {}", deviceId, metersOnDevice);
309 }
310 return metersOnDevice.get(bandwidthProfile) != null &&
311 metersOnDevice.get(bandwidthProfile).getMeterStatus().equals(MeterState.ADDED);
312 } finally {
313 programmedMeterReadLock.unlock();
314 }
315 }
316
317 public boolean hasPendingMeterByBandwidthProfile(DeviceId deviceId, String bandwidthProfile) {
318 try {
319 programmedMeterReadLock.lock();
320 Map<String, MeterData> metersOnDevice = programmedMeters.get(deviceId);
321 if (metersOnDevice == null || metersOnDevice.isEmpty()) {
322 return false;
323 }
324 if (log.isTraceEnabled()) {
325 log.trace("pending metersOnDevice {}: {}", deviceId, metersOnDevice);
326 }
327 // NOTE that we check in order if the meter was ADDED and if it wasn't we check for PENDING_ADD
328 // it is possible that a different thread move the meter state from PENDING_ADD
329 // to ADDED between these two checks
330 // to avoid creating the meter twice we return true event if the meter is already added
331 return metersOnDevice.get(bandwidthProfile) != null && (
332 metersOnDevice.get(bandwidthProfile).getMeterStatus().equals(MeterState.ADDED) ||
333 metersOnDevice.get(bandwidthProfile).getMeterStatus().equals(MeterState.PENDING_ADD)
334 );
335
336 } finally {
337 programmedMeterReadLock.unlock();
338 }
339 }
340
341 public MeterId getMeterIdForBandwidthProfile(DeviceId deviceId, String bandwidthProfile) {
342 try {
343 programmedMeterReadLock.lock();
344 Map<String, MeterData> metersOnDevice = programmedMeters.get(deviceId);
345 if (metersOnDevice == null || metersOnDevice.isEmpty()) {
346 return null;
347 }
348 MeterData meterData = metersOnDevice.get(bandwidthProfile);
349 if (meterData == null || meterData.getMeterStatus() != MeterState.ADDED) {
350 return null;
351 }
352 if (log.isTraceEnabled()) {
353 log.debug("Found meter {} on device {} for bandwidth profile {}",
354 meterData.getMeterId(), deviceId, bandwidthProfile);
355 }
356 return meterData.getMeterId();
357 } finally {
358 programmedMeterReadLock.unlock();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000359 }
360 }
361
362 @Override
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700363 public void purgeDeviceMeters(DeviceId deviceId) {
364 log.debug("Purging meters on device {}", deviceId);
365 meterService.purgeMeters(deviceId);
366
367 // after we purge the meters we also need to clear the map
368 try {
369 programmedMeterWriteLock.lock();
370 programmedMeters.remove(deviceId);
371 } finally {
372 programmedMeterWriteLock.unlock();
373 }
374
375 // and clear the event count
376 // NOTE do we need a lock?
377 pendingRemoveMeters.remove(deviceId);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000378 }
379
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700380 /**
381 * Creates of a meter for a given Bandwidth Profile on a given device.
382 *
383 * @param deviceId the DeviceId
384 * @param bandwidthProfile the BandwidthProfile ID
385 */
386 public void createMeterForBp(DeviceId deviceId, String bandwidthProfile) {
387 // adding meter in pending state to the programmedMeter map
388 try {
389 programmedMeterWriteLock.lock();
390 programmedMeters.compute(deviceId, (d, deviceMeters) -> {
391
392 if (deviceMeters == null) {
393 deviceMeters = new HashMap<>();
394 }
395 // NOTE that this method is only called after verifying a
396 // meter for this BP does not already exist
397 MeterData meterData = new MeterData(
398 null,
399 MeterState.PENDING_ADD,
400 bandwidthProfile
401 );
402 deviceMeters.put(bandwidthProfile, meterData);
403
404 return deviceMeters;
405 });
406 } finally {
407 programmedMeterWriteLock.unlock();
408 }
409
410 BandwidthProfileInformation bpInfo = getBandwidthProfileInformation(bandwidthProfile);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000411 if (bpInfo == null) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700412 log.error("BandwidthProfile {} information not found in sadis", bandwidthProfile);
413 return;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000414 }
415
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700416 log.info("Creating meter for BandwidthProfile {} on device {}", bpInfo.id(), deviceId);
417
418 if (log.isTraceEnabled()) {
419 log.trace("BandwidthProfile: {}", bpInfo);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000420 }
Andrea Campanella672485f2022-03-17 11:50:56 +0100421 try {
422 List<Band> meterBands = createMeterBands(bpInfo);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000423
Andrea Campanella672485f2022-03-17 11:50:56 +0100424 log.info("Meter bands {} for bwp {}", meterBands, bpInfo);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000425
Andrea Campanella672485f2022-03-17 11:50:56 +0100426 CompletableFuture<Object> meterFuture = new CompletableFuture<>();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700427
Andrea Campanella672485f2022-03-17 11:50:56 +0100428 MeterRequest meterRequest = DefaultMeterRequest.builder()
429 .withBands(meterBands)
430 .withUnit(Meter.Unit.KB_PER_SEC)
431 .withContext(new MeterContext() {
432 @Override
433 public void onSuccess(MeterRequest op) {
434 log.info("Meter for BandwidthProfile {} is installed on the device {}",
435 bandwidthProfile, deviceId);
436 meterFuture.complete(null);
437 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000438
Andrea Campanella672485f2022-03-17 11:50:56 +0100439 @Override
440 public void onError(MeterRequest op, MeterFailReason reason) {
441 log.error("Failed installing meter on {} for {}",
442 deviceId, bandwidthProfile);
443 meterFuture.complete(reason);
444 }
445 })
446 .forDevice(deviceId)
447 .fromApp(appId)
448 .burst()
449 .add();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000450
Andrea Campanella672485f2022-03-17 11:50:56 +0100451 // creating the meter
452 Meter meter = meterService.submit(meterRequest);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000453
Andrea Campanella672485f2022-03-17 11:50:56 +0100454 // wait for the meter to be completed
455 meterFuture.thenAccept(error -> {
456 if (error != null) {
457 log.error("Cannot create meter, TODO address me");
458 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700459
Andrea Campanella672485f2022-03-17 11:50:56 +0100460 // then update the map with the MeterId
461 try {
462 programmedMeterWriteLock.lock();
463 programmedMeters.compute(deviceId, (d, entry) -> {
464 if (entry != null) {
465 entry.compute(bandwidthProfile, (bp, meterData) -> {
466 if (meterData != null) {
467 meterData.setMeterCellId(meter.meterCellId());
468 meterData.setMeterStatus(MeterState.ADDED);
469 }
470 return meterData;
471 });
472 }
473 return entry;
474 });
475 } finally {
476 programmedMeterWriteLock.unlock();
477 }
478 });
479 } catch (Exception e) {
480 log.error("", e);
481 }
Andrea Campanella600d2e22020-06-22 11:00:31 +0200482 }
483
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000484 private List<Band> createMeterBands(BandwidthProfileInformation bpInfo) {
485 List<Band> meterBands = new ArrayList<>();
486
Gamze Abakaf46ab432021-03-03 10:51:17 +0000487 // add cir
488 if (bpInfo.committedInformationRate() != 0) {
Andrea Campanella672485f2022-03-17 11:50:56 +0100489 meterBands.add(createMeterBand(bpInfo.committedInformationRate(),
490 bpInfo.committedBurstSize(), Band.Type.DROP, null));
Gamze Abakaf46ab432021-03-03 10:51:17 +0000491 }
492
493 // check if both air and gir are set together in sadis
494 // if they are, set air to 0
495 if (bpInfo.assuredInformationRate() != 0 && bpInfo.guaranteedInformationRate() != 0) {
496 bpInfo.setAssuredInformationRate(0);
497 }
498
499 // add pir
500 long pir = bpInfo.peakInformationRate() != 0 ? bpInfo.peakInformationRate() : (bpInfo.exceededInformationRate()
501 + bpInfo.committedInformationRate() + bpInfo.guaranteedInformationRate()
502 + bpInfo.assuredInformationRate());
503
504 Long pbs = bpInfo.peakBurstSize() != null ? bpInfo.peakBurstSize() :
505 (bpInfo.exceededBurstSize() != null ? bpInfo.exceededBurstSize() : 0) +
506 (bpInfo.committedBurstSize() != null ? bpInfo.committedBurstSize() : 0);
507
Andrea Campanella672485f2022-03-17 11:50:56 +0100508 meterBands.add(createMeterBand(pir, pbs, Band.Type.REMARK, (short) 1));
Gamze Abakaf46ab432021-03-03 10:51:17 +0000509
510 // add gir
Andrea Campanella672485f2022-03-17 11:50:56 +0100511 //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 +0000512 if (bpInfo.guaranteedInformationRate() != 0) {
Andrea Campanella672485f2022-03-17 11:50:56 +0100513 meterBands.add(createMeterBand(bpInfo.guaranteedInformationRate(), 0L, Band.Type.DROP, null));
Gamze Abakaf46ab432021-03-03 10:51:17 +0000514 }
515
516 // add air
517 // air is used in place of gir only if gir is
518 // not present and air is not 0, see line 330.
519 // Included for backwards compatibility, will be removed in VOLTHA 2.9.
Andrea Campanella672485f2022-03-17 11:50:56 +0100520 // Using Band.Type.NONE is ok because this will be removed.
Gamze Abakaf46ab432021-03-03 10:51:17 +0000521 if (bpInfo.assuredInformationRate() != 0) {
Andrea Campanella672485f2022-03-17 11:50:56 +0100522 meterBands.add(createMeterBand(bpInfo.assuredInformationRate(), 0L, Band.Type.DROP, null));
Gamze Abakaf46ab432021-03-03 10:51:17 +0000523 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000524
525 return meterBands;
526 }
527
Andrea Campanella672485f2022-03-17 11:50:56 +0100528 private Band createMeterBand(long rate, Long burst, Band.Type type, Short precedence) {
529 Band.Builder bandBuilder = DefaultBand.builder()
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000530 .withRate(rate) //already Kbps
531 .burstSize(burst) // already Kbits
Andrea Campanella672485f2022-03-17 11:50:56 +0100532 .ofType(type); // no matter
533 if (precedence != null) {
534 bandBuilder.dropPrecedence(precedence);
535 }
536
537 return bandBuilder.build();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000538 }
539
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700540 private BandwidthProfileInformation getBandwidthProfileInformation(String bandwidthProfile) {
541 if (!checkSadisRunning()) {
542 return null;
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200543 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700544 if (bandwidthProfile == null) {
545 return null;
546 }
547 return bpService.get(bandwidthProfile);
548 }
549
550 private boolean checkSadisRunning() {
551 if (bpService == null) {
552 log.warn("Sadis is not running");
553 return false;
554 }
555 return true;
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200556 }
557
Andrea Campanella600d2e22020-06-22 11:00:31 +0200558 private class InternalMeterListener implements MeterListener {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000559 @Override
560 public void event(MeterEvent meterEvent) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700561 pendingRemovalMetersExecutor.execute(() -> {
562
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000563 Meter meter = meterEvent.subject();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700564 if (!appId.equals(meter.appId())) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000565 return;
566 }
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200567
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700568 if (log.isTraceEnabled()) {
569 log.trace("Received meter event {}", meterEvent);
570 }
571 MeterKey key = MeterKey.key(meter.deviceId(), meter.id());
572 if (meterEvent.type().equals(MeterEvent.Type.METER_REFERENCE_COUNT_ZERO)) {
573 if (!oltDeviceService.isLocalLeader(meter.deviceId())) {
574 if (log.isTraceEnabled()) {
575 log.trace("ignoring meter event {} " +
576 "as not leader for {}", meterEvent, meter.deviceId());
577 }
578 return;
579 }
580 log.info("Zero Count Reference event is received for meter {} on {}, " +
581 "incrementing counter",
582 meter.id(), meter.deviceId());
583 incrementMeterCount(meter.deviceId(), key);
584 if (pendingRemoveMeters.get(meter.deviceId())
585 .get(key).get() == zeroReferenceMeterCount) {
586 // only delete the meters if the app is configured to do so
587 if (deleteMeters) {
588 log.info("Meter {} on device {} is unused, removing it", meter.id(), meter.deviceId());
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200589 deleteMeter(meter.deviceId(), meter.id());
590 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000591 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700592 }
593
594 if (meterEvent.type().equals(MeterEvent.Type.METER_REMOVED)) {
595 removeMeterCount(meter, key);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000596 }
597 });
598 }
599
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700600 private void removeMeterCount(Meter meter, MeterKey key) {
601 pendingRemoveMeters.computeIfPresent(meter.deviceId(),
602 (id, meters) -> {
603 if (meters.get(key) == null) {
604 log.info("Meters is not pending " +
605 "{} on {}", key, id);
606 return meters;
607 }
608 meters.remove(key);
609 return meters;
610 });
611 }
612
Andrea Campanella600d2e22020-06-22 11:00:31 +0200613 private void incrementMeterCount(DeviceId deviceId, MeterKey key) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000614 if (key == null) {
615 return;
616 }
Andrea Campanella600d2e22020-06-22 11:00:31 +0200617 pendingRemoveMeters.compute(deviceId,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700618 (id, meters) -> {
619 if (meters == null) {
620 meters = new HashMap<>();
Andrea Campanella600d2e22020-06-22 11:00:31 +0200621
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700622 }
623 if (meters.get(key) == null) {
624 meters.put(key, new AtomicInteger(1));
625 }
626 meters.get(key).addAndGet(1);
627 return meters;
628 });
629 }
630 }
631
632 private void deleteMeter(DeviceId deviceId, MeterId meterId) {
633 Meter meter = meterService.getMeter(deviceId, meterId);
634 if (meter != null) {
635 MeterRequest meterRequest = DefaultMeterRequest.builder()
636 .withBands(meter.bands())
637 .withUnit(meter.unit())
638 .forDevice(deviceId)
639 .fromApp(appId)
640 .burst()
641 .remove();
642
643 meterService.withdraw(meterRequest, meterId);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000644 }
645
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700646 // remove the meter from local caching
647 try {
648 programmedMeterWriteLock.lock();
649 programmedMeters.computeIfPresent(deviceId, (d, deviceMeters) -> {
650 Iterator<Map.Entry<String, MeterData>> iter = deviceMeters.entrySet().iterator();
651 while (iter.hasNext()) {
652 Map.Entry<String, MeterData> entry = iter.next();
653 if (entry.getValue().getMeterId().equals(meterId)) {
654 deviceMeters.remove(entry.getKey());
655 }
656 }
657 return deviceMeters;
658 });
659 } finally {
660 programmedMeterWriteLock.unlock();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000661 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000662 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700663
664 protected void bindSadisService(SadisService service) {
665 this.bpService = service.getBandwidthProfileService();
666 log.info("Sadis service is loaded");
667 }
668
669 protected void unbindSadisService(SadisService service) {
670 this.bpService = null;
671 log.info("Sadis service is unloaded");
672 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000673}