blob: c6f270918a15015915a3e40a1791a67f323424b5 [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;
Gustavo Silva29fb20e2022-05-26 09:59:54 -030042import org.opencord.olt.MeterData;
43import org.opencord.olt.OltDeviceServiceInterface;
44import org.opencord.olt.OltMeterServiceInterface;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000045import org.opencord.sadis.BandwidthProfileInformation;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070046import org.opencord.sadis.BaseInformationService;
47import org.opencord.sadis.SadisService;
48import org.opencord.sadis.SubscriberAndDeviceInformation;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000049import org.osgi.service.component.ComponentContext;
50import org.osgi.service.component.annotations.Activate;
51import org.osgi.service.component.annotations.Component;
52import org.osgi.service.component.annotations.Deactivate;
53import org.osgi.service.component.annotations.Modified;
54import org.osgi.service.component.annotations.Reference;
55import org.osgi.service.component.annotations.ReferenceCardinality;
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070056import org.osgi.service.component.annotations.ReferencePolicy;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000057import org.slf4j.Logger;
58
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070059import java.util.ArrayList;
60import java.util.Dictionary;
61import java.util.HashMap;
62import java.util.Iterator;
63import java.util.LinkedList;
64import java.util.List;
65import java.util.Map;
66import java.util.Properties;
67import java.util.concurrent.CompletableFuture;
68import java.util.concurrent.ExecutorService;
69import java.util.concurrent.Executors;
70import java.util.concurrent.atomic.AtomicBoolean;
71import java.util.concurrent.atomic.AtomicInteger;
72import java.util.concurrent.locks.Lock;
73import java.util.concurrent.locks.ReentrantReadWriteLock;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000074
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070075import static com.google.common.base.Strings.isNullOrEmpty;
76import static org.onlab.util.Tools.get;
77import static org.onlab.util.Tools.groupedThreads;
78import static org.opencord.olt.impl.OsgiPropertyConstants.*;
79import static org.slf4j.LoggerFactory.getLogger;
80
Andrea Campanellacbbb7952019-11-25 06:38:41 +000081@Component(immediate = true, property = {
82 DELETE_METERS + ":Boolean=" + DELETE_METERS_DEFAULT,
Andrea Campanella7e1eb712021-09-22 14:27:35 +020083 ZERO_REFERENCE_METER_COUNT + ":Integer=" + ZERO_REFERENCE_METER_COUNT_DEFAULT,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070084})
85public class OltMeterService implements OltMeterServiceInterface {
Andrea Campanellacbbb7952019-11-25 06:38:41 +000086
87 @Reference(cardinality = ReferenceCardinality.MANDATORY)
88 protected CoreService coreService;
89
90 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070091 protected ComponentConfigService cfgService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000092
Jonathan Hart4f178fa2020-02-03 10:46:01 -080093 @Reference(cardinality = ReferenceCardinality.MANDATORY)
94 protected StorageService storageService;
95
Matteo Scandoloaa2adde2021-09-13 12:45:32 -070096 @Reference(cardinality = ReferenceCardinality.OPTIONAL,
97 bind = "bindSadisService",
98 unbind = "unbindSadisService",
99 policy = ReferencePolicy.DYNAMIC)
100 protected volatile SadisService sadisService;
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700103 protected MeterService meterService;
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700106 protected OltDeviceServiceInterface oltDeviceService;
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200107
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700108 private final Logger log = getLogger(getClass());
109 protected BaseInformationService<BandwidthProfileInformation> bpService;
110 private ApplicationId appId;
111 private static final String APP_NAME = "org.opencord.olt";
112 private final ReentrantReadWriteLock programmedMeterLock = new ReentrantReadWriteLock();
113 private final Lock programmedMeterWriteLock = programmedMeterLock.writeLock();
114 private final Lock programmedMeterReadLock = programmedMeterLock.readLock();
115
116 /**
117 * Programmed Meters status map.
118 * Keeps track of which meter is programmed on which device for which BandwidthProfile.
119 * The String key is the BandwidthProfile
120 */
121 protected Map<DeviceId, Map<String, MeterData>> programmedMeters;
122
123 private final MeterListener meterListener = new InternalMeterListener();
124 protected ExecutorService pendingRemovalMetersExecutor =
125 Executors.newFixedThreadPool(5, groupedThreads("onos/olt",
126 "pending-removal-meters-%d", log));
127
128 /**
129 * Map that contains a list of meters that needs to be removed.
130 * We wait to get 3 METER_REFERENCE_COUNT_ZERO events before removing the meter
131 * so that we're sure no flow is referencing it.
132 */
133 protected Map<DeviceId, Map<MeterKey, AtomicInteger>> pendingRemoveMeters;
134
135 /**
136 * Number of consecutive meter events with empty reference count
137 * after which a meter gets removed from the device.
138 */
139 protected int zeroReferenceMeterCount = 3;
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200140
Saurav Dasf62cea82020-08-26 17:43:04 -0700141 /**
142 * Delete meters when reference count drops to zero.
143 */
144 protected boolean deleteMeters = DELETE_METERS_DEFAULT;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000145
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000146 @Activate
147 public void activate(ComponentContext context) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000148 appId = coreService.registerApplication(APP_NAME);
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800149 modified(context);
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800150 KryoNamespace serializer = KryoNamespace.newBuilder()
151 .register(KryoNamespaces.API)
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700152 .register(List.class)
153 .register(MeterData.class)
154 .register(MeterState.class)
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800155 .register(MeterKey.class)
156 .build();
157
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700158 programmedMeters = storageService.<DeviceId, Map<String, MeterData>>consistentMapBuilder()
159 .withName("volt-programmed-meters")
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000160 .withSerializer(Serializer.using(serializer))
161 .withApplicationId(appId)
162 .build().asJavaMap();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700163
Ilayda Ozdemir90a93622021-02-25 09:40:58 +0000164 pendingRemoveMeters = storageService.<DeviceId, Map<MeterKey, AtomicInteger>>consistentMapBuilder()
165 .withName("volt-pending-remove-meters")
166 .withSerializer(Serializer.using(serializer))
167 .withApplicationId(appId)
168 .build().asJavaMap();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000169
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700170 cfgService.registerProperties(getClass());
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000171
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700172 meterService.addListener(meterListener);
173
174 log.info("Started");
175 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000176
177 @Modified
178 public void modified(ComponentContext context) {
179 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
180
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700181 Boolean d = Tools.isPropertyEnabled(properties, DELETE_METERS);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000182 if (d != null) {
183 deleteMeters = d;
184 }
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200185
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700186 String zeroCount = get(properties, ZERO_REFERENCE_METER_COUNT);
187 int oldSubscriberProcessingThreads = zeroReferenceMeterCount;
188 zeroReferenceMeterCount = isNullOrEmpty(zeroCount) ?
189 oldSubscriberProcessingThreads : Integer.parseInt(zeroCount.trim());
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200190
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700191 log.info("Modified. Values = deleteMeters: {}, zeroReferenceMeterCount: {}",
192 deleteMeters, zeroReferenceMeterCount);
193 }
194
195 @Deactivate
196 public void deactivate(ComponentContext context) {
197 cfgService.unregisterProperties(getClass(), false);
198 meterService.removeListener(meterListener);
199 log.info("Stopped");
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000200 }
201
202 @Override
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700203 public Map<DeviceId, Map<String, MeterData>> getProgrammedMeters() {
204 try {
205 programmedMeterReadLock.lock();
206 return ImmutableMap.copyOf(programmedMeters);
207 } finally {
208 programmedMeterReadLock.unlock();
209 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000210 }
211
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700212 /**
213 * Will create a meter if needed and return true once available.
214 *
215 * @param deviceId DeviceId
216 * @param bandwidthProfile Bandwidth Profile Id
217 * @return true
218 */
219 @Override
220 public synchronized boolean createMeter(DeviceId deviceId, String bandwidthProfile) {
221
222 // NOTE it is possible that hasMeterByBandwidthProfile returns false has the meter is in PENDING_ADD
223 // then a different thread changes the meter to ADDED
224 // and thus hasPendingMeterByBandwidthProfile return false as well and we install the meter a second time
225 // this causes an inconsistency between the existing meter and meterId stored in the map
226
227 if (!hasMeterByBandwidthProfile(deviceId, bandwidthProfile)) {
228 // NOTE this is at trace level as it's constantly called by the queue processor
229 if (log.isTraceEnabled()) {
230 log.trace("Missing meter for BandwidthProfile {} on device {}", bandwidthProfile, deviceId);
231 }
232
233 if (!hasPendingMeterByBandwidthProfile(deviceId, bandwidthProfile)) {
234 createMeterForBp(deviceId, bandwidthProfile);
235 }
236 if (log.isTraceEnabled()) {
237 log.trace("Meter is not yet available for {} on device {}",
238 bandwidthProfile, deviceId);
239 }
240 return false;
241 }
242 log.debug("Meter found for BandwidthProfile {} on device {}", bandwidthProfile, deviceId);
243 return true;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000244 }
245
246 @Override
Matteo Scandolo88df8ae2021-11-23 13:12:29 -0800247 public boolean createMeters(DeviceId deviceId, SubscriberAndDeviceInformation si, String multicastServiceName) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700248 // Each UniTagInformation has up to 4 meters,
249 // check and/or create all of them
250 AtomicBoolean waitingOnMeter = new AtomicBoolean();
251 waitingOnMeter.set(false);
252 Map<String, List<String>> pendingMeters = new HashMap<>();
253 si.uniTagList().forEach(uniTagInfo -> {
254 String serviceName = uniTagInfo.getServiceName();
Matteo Scandolo88df8ae2021-11-23 13:12:29 -0800255
256 if (multicastServiceName.equals(uniTagInfo.getServiceName())) {
257 log.debug("This is the multicast service ({}) for subscriber {} on {}, " +
258 "meters are not needed",
259 uniTagInfo.getServiceName(), si.id(), deviceId);
260 return;
261 }
262
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700263 pendingMeters.put(serviceName, new LinkedList<>());
264 String usBp = uniTagInfo.getUpstreamBandwidthProfile();
265 String dsBp = uniTagInfo.getDownstreamBandwidthProfile();
266 String oltUBp = uniTagInfo.getDownstreamOltBandwidthProfile();
267 String oltDsBp = uniTagInfo.getUpstreamOltBandwidthProfile();
268 if (!createMeter(deviceId, usBp)) {
269 pendingMeters.get(serviceName).add(usBp);
270 waitingOnMeter.set(true);
271 }
272 if (!createMeter(deviceId, dsBp)) {
273 pendingMeters.get(serviceName).add(usBp);
274 waitingOnMeter.set(true);
275 }
276 if (!createMeter(deviceId, oltUBp)) {
277 pendingMeters.get(serviceName).add(usBp);
278 waitingOnMeter.set(true);
279 }
280 if (!createMeter(deviceId, oltDsBp)) {
281 pendingMeters.get(serviceName).add(usBp);
282 waitingOnMeter.set(true);
283 }
284 });
285 if (waitingOnMeter.get()) {
286 if (log.isTraceEnabled()) {
287 log.trace("Meters {} on device {} are not " +
288 "installed yet (requested by subscriber {})",
289 pendingMeters, deviceId, si.id());
290 }
291 return false;
yasin saplib4b8ee12021-06-13 18:25:20 +0000292 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700293 return true;
294 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000295
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700296 /**
297 * Returns true if a meter is present in the programmed meters map, only if status is ADDED.
298 *
299 * @param deviceId the DeviceId on which to look for the meter
300 * @param bandwidthProfile the Bandwidth profile associated with this meter
301 * @return true if the meter is found
302 */
303 public boolean hasMeterByBandwidthProfile(DeviceId deviceId, String bandwidthProfile) {
304 try {
305 programmedMeterReadLock.lock();
306 Map<String, MeterData> metersOnDevice = programmedMeters.get(deviceId);
307 if (metersOnDevice == null || metersOnDevice.isEmpty()) {
308 return false;
309 }
310 if (log.isTraceEnabled()) {
311 log.trace("added metersOnDevice {}: {}", deviceId, metersOnDevice);
312 }
313 return metersOnDevice.get(bandwidthProfile) != null &&
314 metersOnDevice.get(bandwidthProfile).getMeterStatus().equals(MeterState.ADDED);
315 } finally {
316 programmedMeterReadLock.unlock();
317 }
318 }
319
320 public boolean hasPendingMeterByBandwidthProfile(DeviceId deviceId, String bandwidthProfile) {
321 try {
322 programmedMeterReadLock.lock();
323 Map<String, MeterData> metersOnDevice = programmedMeters.get(deviceId);
324 if (metersOnDevice == null || metersOnDevice.isEmpty()) {
325 return false;
326 }
327 if (log.isTraceEnabled()) {
328 log.trace("pending metersOnDevice {}: {}", deviceId, metersOnDevice);
329 }
330 // NOTE that we check in order if the meter was ADDED and if it wasn't we check for PENDING_ADD
331 // it is possible that a different thread move the meter state from PENDING_ADD
332 // to ADDED between these two checks
333 // to avoid creating the meter twice we return true event if the meter is already added
334 return metersOnDevice.get(bandwidthProfile) != null && (
335 metersOnDevice.get(bandwidthProfile).getMeterStatus().equals(MeterState.ADDED) ||
336 metersOnDevice.get(bandwidthProfile).getMeterStatus().equals(MeterState.PENDING_ADD)
337 );
338
339 } finally {
340 programmedMeterReadLock.unlock();
341 }
342 }
343
344 public MeterId getMeterIdForBandwidthProfile(DeviceId deviceId, String bandwidthProfile) {
345 try {
346 programmedMeterReadLock.lock();
347 Map<String, MeterData> metersOnDevice = programmedMeters.get(deviceId);
348 if (metersOnDevice == null || metersOnDevice.isEmpty()) {
349 return null;
350 }
351 MeterData meterData = metersOnDevice.get(bandwidthProfile);
352 if (meterData == null || meterData.getMeterStatus() != MeterState.ADDED) {
353 return null;
354 }
355 if (log.isTraceEnabled()) {
356 log.debug("Found meter {} on device {} for bandwidth profile {}",
357 meterData.getMeterId(), deviceId, bandwidthProfile);
358 }
359 return meterData.getMeterId();
360 } finally {
361 programmedMeterReadLock.unlock();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000362 }
363 }
364
365 @Override
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700366 public void purgeDeviceMeters(DeviceId deviceId) {
367 log.debug("Purging meters on device {}", deviceId);
368 meterService.purgeMeters(deviceId);
369
370 // after we purge the meters we also need to clear the map
371 try {
372 programmedMeterWriteLock.lock();
373 programmedMeters.remove(deviceId);
374 } finally {
375 programmedMeterWriteLock.unlock();
376 }
377
378 // and clear the event count
379 // NOTE do we need a lock?
380 pendingRemoveMeters.remove(deviceId);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000381 }
382
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700383 /**
384 * Creates of a meter for a given Bandwidth Profile on a given device.
385 *
386 * @param deviceId the DeviceId
387 * @param bandwidthProfile the BandwidthProfile ID
388 */
389 public void createMeterForBp(DeviceId deviceId, String bandwidthProfile) {
390 // adding meter in pending state to the programmedMeter map
391 try {
392 programmedMeterWriteLock.lock();
393 programmedMeters.compute(deviceId, (d, deviceMeters) -> {
394
395 if (deviceMeters == null) {
396 deviceMeters = new HashMap<>();
397 }
398 // NOTE that this method is only called after verifying a
399 // meter for this BP does not already exist
400 MeterData meterData = new MeterData(
401 null,
402 MeterState.PENDING_ADD,
403 bandwidthProfile
404 );
405 deviceMeters.put(bandwidthProfile, meterData);
406
407 return deviceMeters;
408 });
409 } finally {
410 programmedMeterWriteLock.unlock();
411 }
412
413 BandwidthProfileInformation bpInfo = getBandwidthProfileInformation(bandwidthProfile);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000414 if (bpInfo == null) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700415 log.error("BandwidthProfile {} information not found in sadis", bandwidthProfile);
416 return;
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000417 }
418
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700419 log.info("Creating meter for BandwidthProfile {} on device {}", bpInfo.id(), deviceId);
420
421 if (log.isTraceEnabled()) {
422 log.trace("BandwidthProfile: {}", bpInfo);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000423 }
Andrea Campanella672485f2022-03-17 11:50:56 +0100424 try {
425 List<Band> meterBands = createMeterBands(bpInfo);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000426
Andrea Campanella672485f2022-03-17 11:50:56 +0100427 log.info("Meter bands {} for bwp {}", meterBands, bpInfo);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000428
Andrea Campanella672485f2022-03-17 11:50:56 +0100429 CompletableFuture<Object> meterFuture = new CompletableFuture<>();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700430
Andrea Campanella672485f2022-03-17 11:50:56 +0100431 MeterRequest meterRequest = DefaultMeterRequest.builder()
432 .withBands(meterBands)
433 .withUnit(Meter.Unit.KB_PER_SEC)
434 .withContext(new MeterContext() {
435 @Override
436 public void onSuccess(MeterRequest op) {
437 log.info("Meter for BandwidthProfile {} is installed on the device {}",
438 bandwidthProfile, deviceId);
439 meterFuture.complete(null);
440 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000441
Andrea Campanella672485f2022-03-17 11:50:56 +0100442 @Override
443 public void onError(MeterRequest op, MeterFailReason reason) {
444 log.error("Failed installing meter on {} for {}",
445 deviceId, bandwidthProfile);
446 meterFuture.complete(reason);
447 }
448 })
449 .forDevice(deviceId)
450 .fromApp(appId)
451 .burst()
452 .add();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000453
Andrea Campanella672485f2022-03-17 11:50:56 +0100454 // creating the meter
455 Meter meter = meterService.submit(meterRequest);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000456
Andrea Campanella672485f2022-03-17 11:50:56 +0100457 // wait for the meter to be completed
458 meterFuture.thenAccept(error -> {
459 if (error != null) {
460 log.error("Cannot create meter, TODO address me");
461 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700462
Andrea Campanella672485f2022-03-17 11:50:56 +0100463 // then update the map with the MeterId
464 try {
465 programmedMeterWriteLock.lock();
466 programmedMeters.compute(deviceId, (d, entry) -> {
467 if (entry != null) {
468 entry.compute(bandwidthProfile, (bp, meterData) -> {
469 if (meterData != null) {
470 meterData.setMeterCellId(meter.meterCellId());
471 meterData.setMeterStatus(MeterState.ADDED);
472 }
473 return meterData;
474 });
475 }
476 return entry;
477 });
478 } finally {
479 programmedMeterWriteLock.unlock();
480 }
481 });
482 } catch (Exception e) {
483 log.error("", e);
484 }
Andrea Campanella600d2e22020-06-22 11:00:31 +0200485 }
486
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000487 private List<Band> createMeterBands(BandwidthProfileInformation bpInfo) {
488 List<Band> meterBands = new ArrayList<>();
489
Gamze Abakaf46ab432021-03-03 10:51:17 +0000490 // add cir
491 if (bpInfo.committedInformationRate() != 0) {
Andrea Campanella672485f2022-03-17 11:50:56 +0100492 meterBands.add(createMeterBand(bpInfo.committedInformationRate(),
493 bpInfo.committedBurstSize(), Band.Type.DROP, null));
Gamze Abakaf46ab432021-03-03 10:51:17 +0000494 }
495
496 // check if both air and gir are set together in sadis
497 // if they are, set air to 0
498 if (bpInfo.assuredInformationRate() != 0 && bpInfo.guaranteedInformationRate() != 0) {
499 bpInfo.setAssuredInformationRate(0);
500 }
501
502 // add pir
503 long pir = bpInfo.peakInformationRate() != 0 ? bpInfo.peakInformationRate() : (bpInfo.exceededInformationRate()
504 + bpInfo.committedInformationRate() + bpInfo.guaranteedInformationRate()
505 + bpInfo.assuredInformationRate());
506
507 Long pbs = bpInfo.peakBurstSize() != null ? bpInfo.peakBurstSize() :
508 (bpInfo.exceededBurstSize() != null ? bpInfo.exceededBurstSize() : 0) +
509 (bpInfo.committedBurstSize() != null ? bpInfo.committedBurstSize() : 0);
510
Andrea Campanella672485f2022-03-17 11:50:56 +0100511 meterBands.add(createMeterBand(pir, pbs, Band.Type.REMARK, (short) 1));
Gamze Abakaf46ab432021-03-03 10:51:17 +0000512
513 // add gir
Andrea Campanella672485f2022-03-17 11:50:56 +0100514 //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 +0000515 if (bpInfo.guaranteedInformationRate() != 0) {
Andrea Campanella672485f2022-03-17 11:50:56 +0100516 meterBands.add(createMeterBand(bpInfo.guaranteedInformationRate(), 0L, Band.Type.DROP, null));
Gamze Abakaf46ab432021-03-03 10:51:17 +0000517 }
518
519 // add air
520 // air is used in place of gir only if gir is
521 // not present and air is not 0, see line 330.
522 // Included for backwards compatibility, will be removed in VOLTHA 2.9.
Andrea Campanella672485f2022-03-17 11:50:56 +0100523 // Using Band.Type.NONE is ok because this will be removed.
Gamze Abakaf46ab432021-03-03 10:51:17 +0000524 if (bpInfo.assuredInformationRate() != 0) {
Andrea Campanella672485f2022-03-17 11:50:56 +0100525 meterBands.add(createMeterBand(bpInfo.assuredInformationRate(), 0L, Band.Type.DROP, null));
Gamze Abakaf46ab432021-03-03 10:51:17 +0000526 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000527
528 return meterBands;
529 }
530
Andrea Campanella672485f2022-03-17 11:50:56 +0100531 private Band createMeterBand(long rate, Long burst, Band.Type type, Short precedence) {
532 Band.Builder bandBuilder = DefaultBand.builder()
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000533 .withRate(rate) //already Kbps
534 .burstSize(burst) // already Kbits
Andrea Campanella672485f2022-03-17 11:50:56 +0100535 .ofType(type); // no matter
536 if (precedence != null) {
537 bandBuilder.dropPrecedence(precedence);
538 }
539
540 return bandBuilder.build();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000541 }
542
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700543 private BandwidthProfileInformation getBandwidthProfileInformation(String bandwidthProfile) {
544 if (!checkSadisRunning()) {
545 return null;
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200546 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700547 if (bandwidthProfile == null) {
548 return null;
549 }
550 return bpService.get(bandwidthProfile);
551 }
552
553 private boolean checkSadisRunning() {
554 if (bpService == null) {
555 log.warn("Sadis is not running");
556 return false;
557 }
558 return true;
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200559 }
560
Andrea Campanella600d2e22020-06-22 11:00:31 +0200561 private class InternalMeterListener implements MeterListener {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000562 @Override
563 public void event(MeterEvent meterEvent) {
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700564 pendingRemovalMetersExecutor.execute(() -> {
565
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000566 Meter meter = meterEvent.subject();
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700567 if (!appId.equals(meter.appId())) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000568 return;
569 }
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200570
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700571 if (log.isTraceEnabled()) {
572 log.trace("Received meter event {}", meterEvent);
573 }
574 MeterKey key = MeterKey.key(meter.deviceId(), meter.id());
575 if (meterEvent.type().equals(MeterEvent.Type.METER_REFERENCE_COUNT_ZERO)) {
576 if (!oltDeviceService.isLocalLeader(meter.deviceId())) {
577 if (log.isTraceEnabled()) {
578 log.trace("ignoring meter event {} " +
579 "as not leader for {}", meterEvent, meter.deviceId());
580 }
581 return;
582 }
583 log.info("Zero Count Reference event is received for meter {} on {}, " +
584 "incrementing counter",
585 meter.id(), meter.deviceId());
586 incrementMeterCount(meter.deviceId(), key);
587 if (pendingRemoveMeters.get(meter.deviceId())
588 .get(key).get() == zeroReferenceMeterCount) {
589 // only delete the meters if the app is configured to do so
590 if (deleteMeters) {
591 log.info("Meter {} on device {} is unused, removing it", meter.id(), meter.deviceId());
Andrea Campanella7e1eb712021-09-22 14:27:35 +0200592 deleteMeter(meter.deviceId(), meter.id());
593 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000594 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700595 }
596
597 if (meterEvent.type().equals(MeterEvent.Type.METER_REMOVED)) {
598 removeMeterCount(meter, key);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000599 }
600 });
601 }
602
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700603 private void removeMeterCount(Meter meter, MeterKey key) {
604 pendingRemoveMeters.computeIfPresent(meter.deviceId(),
605 (id, meters) -> {
606 if (meters.get(key) == null) {
607 log.info("Meters is not pending " +
608 "{} on {}", key, id);
609 return meters;
610 }
611 meters.remove(key);
612 return meters;
613 });
614 }
615
Andrea Campanella600d2e22020-06-22 11:00:31 +0200616 private void incrementMeterCount(DeviceId deviceId, MeterKey key) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000617 if (key == null) {
618 return;
619 }
Andrea Campanella600d2e22020-06-22 11:00:31 +0200620 pendingRemoveMeters.compute(deviceId,
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700621 (id, meters) -> {
622 if (meters == null) {
623 meters = new HashMap<>();
Andrea Campanella600d2e22020-06-22 11:00:31 +0200624
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700625 }
626 if (meters.get(key) == null) {
627 meters.put(key, new AtomicInteger(1));
628 }
629 meters.get(key).addAndGet(1);
630 return meters;
631 });
632 }
633 }
634
635 private void deleteMeter(DeviceId deviceId, MeterId meterId) {
636 Meter meter = meterService.getMeter(deviceId, meterId);
637 if (meter != null) {
638 MeterRequest meterRequest = DefaultMeterRequest.builder()
639 .withBands(meter.bands())
640 .withUnit(meter.unit())
641 .forDevice(deviceId)
642 .fromApp(appId)
643 .burst()
644 .remove();
645
646 meterService.withdraw(meterRequest, meterId);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000647 }
648
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700649 // remove the meter from local caching
650 try {
651 programmedMeterWriteLock.lock();
652 programmedMeters.computeIfPresent(deviceId, (d, deviceMeters) -> {
653 Iterator<Map.Entry<String, MeterData>> iter = deviceMeters.entrySet().iterator();
654 while (iter.hasNext()) {
655 Map.Entry<String, MeterData> entry = iter.next();
656 if (entry.getValue().getMeterId().equals(meterId)) {
657 deviceMeters.remove(entry.getKey());
658 }
659 }
660 return deviceMeters;
661 });
662 } finally {
663 programmedMeterWriteLock.unlock();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000664 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000665 }
Matteo Scandoloaa2adde2021-09-13 12:45:32 -0700666
667 protected void bindSadisService(SadisService service) {
668 this.bpService = service.getBandwidthProfileService();
669 log.info("Sadis service is loaded");
670 }
671
672 protected void unbindSadisService(SadisService service) {
673 this.bpService = null;
674 log.info("Sadis service is unloaded");
675 }
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000676}