blob: 65a72f0ce5d0c3c9c2dbb52b8eea4b3c7be740b8 [file] [log] [blame]
Andrea Campanellacbbb7952019-11-25 06:38:41 +00001/*
2 * Copyright 2016-present Open Networking Foundation
3 *
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 */
16package org.opencord.olt.impl;
17
Andrea Campanellaaf39b4c2020-05-13 14:07:44 +020018import com.google.common.collect.ImmutableList;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000019import com.google.common.collect.ImmutableMap;
20import com.google.common.collect.ImmutableSet;
21import com.google.common.collect.Maps;
Jonathan Hart4f178fa2020-02-03 10:46:01 -080022import org.onlab.util.KryoNamespace;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000023import org.onlab.util.Tools;
24import org.onosproject.cfg.ComponentConfigService;
25import org.onosproject.core.ApplicationId;
26import org.onosproject.core.CoreService;
27import org.onosproject.net.DeviceId;
28import org.onosproject.net.flowobjective.ObjectiveError;
29import org.onosproject.net.meter.Band;
30import org.onosproject.net.meter.DefaultBand;
31import org.onosproject.net.meter.DefaultMeterRequest;
32import org.onosproject.net.meter.Meter;
33import org.onosproject.net.meter.MeterContext;
34import org.onosproject.net.meter.MeterEvent;
35import org.onosproject.net.meter.MeterFailReason;
36import org.onosproject.net.meter.MeterId;
37import org.onosproject.net.meter.MeterKey;
38import org.onosproject.net.meter.MeterListener;
39import org.onosproject.net.meter.MeterRequest;
40import org.onosproject.net.meter.MeterService;
Jonathan Hart4f178fa2020-02-03 10:46:01 -080041import org.onosproject.store.serializers.KryoNamespaces;
42import org.onosproject.store.service.ConsistentMultimap;
43import org.onosproject.store.service.Serializer;
44import org.onosproject.store.service.StorageService;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000045import org.opencord.olt.internalapi.AccessDeviceMeterService;
Andrea Campanella3ce4d282020-06-09 13:46:58 +020046import org.opencord.olt.internalapi.DeviceBandwidthProfile;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000047import org.opencord.sadis.BandwidthProfileInformation;
48import org.osgi.service.component.ComponentContext;
49import org.osgi.service.component.annotations.Activate;
50import org.osgi.service.component.annotations.Component;
51import org.osgi.service.component.annotations.Deactivate;
52import org.osgi.service.component.annotations.Modified;
53import org.osgi.service.component.annotations.Reference;
54import org.osgi.service.component.annotations.ReferenceCardinality;
55import org.slf4j.Logger;
56
57import java.util.ArrayList;
58import java.util.Collection;
59import java.util.Dictionary;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000060import java.util.List;
61import java.util.Map;
62import java.util.Optional;
63import java.util.Properties;
Andrea Campanella3ce4d282020-06-09 13:46:58 +020064import java.util.Set;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000065import java.util.concurrent.CompletableFuture;
Andrea Campanella3ce4d282020-06-09 13:46:58 +020066import java.util.concurrent.ConcurrentHashMap;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000067import java.util.concurrent.ExecutorService;
68import java.util.concurrent.Executors;
69import java.util.concurrent.atomic.AtomicInteger;
70import java.util.concurrent.atomic.AtomicReference;
Jonathan Hart4f178fa2020-02-03 10:46:01 -080071import java.util.stream.Collectors;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000072
Jonathan Hart4f178fa2020-02-03 10:46:01 -080073import static java.util.stream.Collectors.collectingAndThen;
74import static java.util.stream.Collectors.groupingBy;
75import static java.util.stream.Collectors.mapping;
76import static java.util.stream.Collectors.toSet;
Andrea Campanellacbbb7952019-11-25 06:38:41 +000077import static org.onlab.util.Tools.groupedThreads;
78import static org.opencord.olt.impl.OsgiPropertyConstants.DELETE_METERS;
79import static org.opencord.olt.impl.OsgiPropertyConstants.DELETE_METERS_DEFAULT;
80import static org.slf4j.LoggerFactory.getLogger;
81
82/**
83 * Provisions Meters on access devices.
84 */
85@Component(immediate = true, property = {
86 DELETE_METERS + ":Boolean=" + DELETE_METERS_DEFAULT,
87 })
88public class OltMeterService implements AccessDeviceMeterService {
89
90 @Reference(cardinality = ReferenceCardinality.MANDATORY)
91 protected MeterService meterService;
92
93 @Reference(cardinality = ReferenceCardinality.MANDATORY)
94 protected CoreService coreService;
95
96 @Reference(cardinality = ReferenceCardinality.MANDATORY)
97 protected ComponentConfigService componentConfigService;
98
Jonathan Hart4f178fa2020-02-03 10:46:01 -080099 @Reference(cardinality = ReferenceCardinality.MANDATORY)
100 protected StorageService storageService;
101
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000102 protected boolean deleteMeters = true;
103
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800104 ConsistentMultimap<String, MeterKey> bpInfoToMeter;
105
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000106 private ApplicationId appId;
107 private static final String APP_NAME = "org.opencord.olt";
108
109 private final MeterListener meterListener = new InternalMeterListener();
110
111 private final Logger log = getLogger(getClass());
112
113 protected ExecutorService eventExecutor;
114
Andrea Campanella3ce4d282020-06-09 13:46:58 +0200115 private Set<DeviceBandwidthProfile> pendingMeters;
116
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000117 @Activate
118 public void activate(ComponentContext context) {
119 eventExecutor = Executors.newFixedThreadPool(5, groupedThreads("onos/olt",
120 "events-%d", log));
121 appId = coreService.registerApplication(APP_NAME);
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800122 modified(context);
123
124 KryoNamespace serializer = KryoNamespace.newBuilder()
125 .register(KryoNamespaces.API)
126 .register(MeterKey.class)
127 .build();
128
129 bpInfoToMeter = storageService.<String, MeterKey>consistentMultimapBuilder()
130 .withName("volt-bp-info-to-meter")
131 .withSerializer(Serializer.using(serializer))
132 .withApplicationId(appId)
133 .build();
134
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000135 meterService.addListener(meterListener);
136 componentConfigService.registerProperties(getClass());
Andrea Campanella3ce4d282020-06-09 13:46:58 +0200137 pendingMeters = ConcurrentHashMap.newKeySet();
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000138 log.info("Olt Meter service started");
139 }
140
141 @Deactivate
142 public void deactivate() {
143 meterService.removeListener(meterListener);
144 }
145
146
147 @Modified
148 public void modified(ComponentContext context) {
149 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
150
151 Boolean d = Tools.isPropertyEnabled(properties, "deleteMeters");
152 if (d != null) {
153 deleteMeters = d;
154 }
155 }
156
157 @Override
158 public ImmutableMap<String, Collection<MeterKey>> getBpMeterMappings() {
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800159 return bpInfoToMeter.stream()
160 .collect(collectingAndThen(
161 groupingBy(Map.Entry::getKey, mapping(Map.Entry::getValue, toSet())),
162 ImmutableMap::copyOf));
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000163 }
164
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800165 void addMeterIdToBpMapping(DeviceId deviceId, MeterId meterId, String bandwidthProfile) {
Andrea Campanella0c3309d2020-05-29 01:51:18 -0700166 log.debug("adding bp {} to meter {} mapping for device {}",
167 bandwidthProfile, meterId, deviceId);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000168 bpInfoToMeter.put(bandwidthProfile, MeterKey.key(deviceId, meterId));
169 }
170
171 @Override
172 public MeterId getMeterIdFromBpMapping(DeviceId deviceId, String bandwidthProfile) {
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800173 if (bpInfoToMeter.get(bandwidthProfile).value().isEmpty()) {
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000174 log.warn("Bandwidth Profile '{}' is not currently mapped to a meter",
175 bandwidthProfile);
176 return null;
177 }
178
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800179 Optional<? extends MeterKey> meterKeyForDevice = bpInfoToMeter.get(bandwidthProfile).value()
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000180 .stream()
181 .filter(meterKey -> meterKey.deviceId().equals(deviceId))
182 .findFirst();
183 if (meterKeyForDevice.isPresent()) {
184 log.debug("Found meter {} for bandwidth profile {}",
185 meterKeyForDevice.get().meterId(), bandwidthProfile);
186 return meterKeyForDevice.get().meterId();
187 } else {
Andrea Campanella0c3309d2020-05-29 01:51:18 -0700188 log.warn("Bandwidth Profile '{}' is not currently mapped to a meter in {}",
189 bandwidthProfile, bpInfoToMeter.get(bandwidthProfile).value());
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000190 return null;
191 }
192 }
193
194 @Override
195 public ImmutableSet<MeterKey> getProgMeters() {
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800196 return bpInfoToMeter.stream()
197 .map(Map.Entry::getValue)
198 .collect(ImmutableSet.toImmutableSet());
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000199 }
200
201 @Override
202 public MeterId createMeter(DeviceId deviceId, BandwidthProfileInformation bpInfo,
203 CompletableFuture<Object> meterFuture) {
Andrea Campanella3ce4d282020-06-09 13:46:58 +0200204 log.debug("Installing meter on {} for {}", deviceId, bpInfo);
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000205 if (bpInfo == null) {
206 log.warn("Requested bandwidth profile information is NULL");
207 meterFuture.complete(ObjectiveError.BADPARAMS);
208 return null;
209 }
210
211 MeterId meterId = getMeterIdFromBpMapping(deviceId, bpInfo.id());
212 if (meterId != null) {
213 log.debug("Meter {} was previously created for bp {}", meterId, bpInfo.id());
214 meterFuture.complete(null);
215 return meterId;
216 }
217
218 List<Band> meterBands = createMeterBands(bpInfo);
219
220 final AtomicReference<MeterId> meterIdRef = new AtomicReference<>();
221 MeterRequest meterRequest = DefaultMeterRequest.builder()
222 .withBands(meterBands)
223 .withUnit(Meter.Unit.KB_PER_SEC)
224 .withContext(new MeterContext() {
225 @Override
226 public void onSuccess(MeterRequest op) {
Andrea Campanella0c3309d2020-05-29 01:51:18 -0700227 log.debug("Meter {} is installed on the device {}",
Andrea Campanella3ce4d282020-06-09 13:46:58 +0200228 meterIdRef.get(), deviceId);
Andrea Campanella0c3309d2020-05-29 01:51:18 -0700229 addMeterIdToBpMapping(deviceId, meterIdRef.get(), bpInfo.id());
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000230 meterFuture.complete(null);
231 }
232
233 @Override
234 public void onError(MeterRequest op, MeterFailReason reason) {
Andrea Campanellac727a372020-06-09 17:34:38 +0200235 log.error("Failed installing meter {} on {} for {}",
236 meterIdRef.get(), deviceId, bpInfo.id());
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000237 bpInfoToMeter.remove(bpInfo.id(),
238 MeterKey.key(deviceId, meterIdRef.get()));
239 meterFuture.complete(reason);
240 }
241 })
242 .forDevice(deviceId)
243 .fromApp(appId)
244 .burst()
245 .add();
246
247 Meter meter = meterService.submit(meterRequest);
248 meterIdRef.set(meter.id());
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000249 log.info("Meter is created. Meter Id {}", meter.id());
250 return meter.id();
251 }
252
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800253 @Override
Andrea Campanella3ce4d282020-06-09 13:46:58 +0200254 public void addToPendingMeters(DeviceBandwidthProfile deviceBandwidthProfile) {
255 pendingMeters.add(deviceBandwidthProfile);
256 }
257
258 @Override
259 public void removeFromPendingMeters(DeviceBandwidthProfile deviceBandwidthProfile) {
260 pendingMeters.remove(deviceBandwidthProfile);
261 }
262
263 @Override
264 public boolean isMeterPending(DeviceBandwidthProfile deviceBandwidthProfile) {
265 return pendingMeters.contains(deviceBandwidthProfile);
266 }
267
268 @Override
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800269 public void clearMeters(DeviceId deviceId) {
270 List<Map.Entry<String, MeterKey>> meters = bpInfoToMeter.stream()
271 .filter(e -> e.getValue().deviceId().equals(deviceId))
272 .collect(Collectors.toList());
273
Andrea Campanellac727a372020-06-09 17:34:38 +0200274 //TODO move to purgeMeters from ONOS 2.2.3-SNAPSHOT
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800275 meters.forEach(e -> bpInfoToMeter.remove(e.getKey(), e.getValue()));
Andrea Campanellaaf39b4c2020-05-13 14:07:44 +0200276 List<Meter> metersToRemove = ImmutableList.copyOf(meterService.getMeters(deviceId));
277 metersToRemove.forEach(meter -> {
278 MeterRequest mq = DefaultMeterRequest.builder().fromApp(appId)
279 .forDevice(deviceId).withBands(meter.bands())
Andrea Campanellac727a372020-06-09 17:34:38 +0200280 .withUnit(meter.unit())
281 .withContext(new MeterContext() {
282 @Override
283 public void onSuccess(MeterRequest op) {
284 log.debug("Meter {} is removed from the device {}",
285 meter.meterCellId(), deviceId);
286 }
287
288 @Override
289 public void onError(MeterRequest op, MeterFailReason reason) {
290 log.error("Meter {} failed to be removed from the device {}",
291 meter.meterCellId(), deviceId);
292 }
293 })
294 .burst()
295 .remove();
Andrea Campanellaaf39b4c2020-05-13 14:07:44 +0200296 meterService.withdraw(mq, meter.id());
297 });
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800298 }
299
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000300 private List<Band> createMeterBands(BandwidthProfileInformation bpInfo) {
301 List<Band> meterBands = new ArrayList<>();
302
303 meterBands.add(createMeterBand(bpInfo.committedInformationRate(), bpInfo.committedBurstSize()));
304 meterBands.add(createMeterBand(bpInfo.exceededInformationRate(), bpInfo.exceededBurstSize()));
305 meterBands.add(createMeterBand(bpInfo.assuredInformationRate(), 0L));
306
307 return meterBands;
308 }
309
310 private Band createMeterBand(long rate, Long burst) {
311 return DefaultBand.builder()
312 .withRate(rate) //already Kbps
313 .burstSize(burst) // already Kbits
314 .ofType(Band.Type.DROP) // no matter
315 .build();
316 }
317
318 private class InternalMeterListener implements MeterListener {
319
320 Map<MeterKey, AtomicInteger> pendingRemoveMeters = Maps.newConcurrentMap();
321
322 @Override
323 public void event(MeterEvent meterEvent) {
324 eventExecutor.execute(() -> {
325 Meter meter = meterEvent.subject();
326 if (meter == null) {
327 log.error("Meter in event {} is null", meterEvent);
328 return;
329 }
330 MeterKey key = MeterKey.key(meter.deviceId(), meter.id());
331 if (deleteMeters && MeterEvent.Type.METER_REFERENCE_COUNT_ZERO.equals(meterEvent.type())) {
332 log.info("Zero Count Meter Event is received. Meter is {}", meter.id());
333 incrementMeterCount(key);
334
335 if (appId.equals(meter.appId()) && pendingRemoveMeters.get(key).get() == 3) {
336 log.info("Deleting unreferenced, no longer programmed Meter {}", meter.id());
337 deleteMeter(meter.deviceId(), meter.id());
338 }
339 }
340 if (MeterEvent.Type.METER_REMOVED.equals(meterEvent.type())) {
341 log.info("Meter Removed Event is received for {}", meter.id());
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000342 pendingRemoveMeters.remove(key);
343 removeMeterFromBpMapping(key);
344 }
345 });
346 }
347
348 private void incrementMeterCount(MeterKey key) {
349 if (key == null) {
350 return;
351 }
352 pendingRemoveMeters.compute(key,
353 (k, v) -> {
354 if (v == null) {
355 return new AtomicInteger(1);
356 }
357 v.addAndGet(1);
358 return v;
359 });
360 }
361
362 private void deleteMeter(DeviceId deviceId, MeterId meterId) {
363 Meter meter = meterService.getMeter(deviceId, meterId);
364 if (meter != null) {
365 MeterRequest meterRequest = DefaultMeterRequest.builder()
366 .withBands(meter.bands())
367 .withUnit(meter.unit())
368 .forDevice(deviceId)
369 .fromApp(appId)
370 .burst()
371 .remove();
372
373 meterService.withdraw(meterRequest, meterId);
374 }
375 }
376
377 private void removeMeterFromBpMapping(MeterKey meterKey) {
Jonathan Hart4f178fa2020-02-03 10:46:01 -0800378 List<Map.Entry<String, MeterKey>> meters = bpInfoToMeter.stream()
379 .filter(e -> e.getValue().equals(meterKey))
380 .collect(Collectors.toList());
381
382 meters.forEach(e -> bpInfoToMeter.remove(e.getKey(), e.getValue()));
Andrea Campanellacbbb7952019-11-25 06:38:41 +0000383 }
384 }
385}