blob: 805009dfc816c5349f4269be8c7a0364a185f728 [file] [log] [blame]
alshabib1d2bc402015-07-31 17:04:11 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
alshabib1d2bc402015-07-31 17:04:11 -07003 *
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 */
Ray Milkeyebdd4852017-10-18 16:19:28 -070016package org.onosproject.net.meter.impl;
alshabib1d2bc402015-07-31 17:04:11 -070017
pierventre44220052020-09-22 12:51:06 +020018import com.google.common.collect.Sets;
pierventre1b8afbc2020-07-13 14:07:05 +020019import org.onlab.util.PredictableExecutor;
20import org.onlab.util.PredictableExecutor.PickyRunnable;
Gamze Abakaf57ef602019-03-11 06:52:48 +000021import org.onlab.util.Tools;
alshabibeadfc8e2015-08-18 15:40:46 -070022import org.onlab.util.TriConsumer;
Andrea Campanellae3708782017-10-16 16:00:21 +020023import org.onosproject.cfg.ComponentConfigService;
pierventre1b8afbc2020-07-13 14:07:05 +020024import org.onosproject.cluster.ClusterService;
25import org.onosproject.cluster.NodeId;
Daniele Moro43ac2892021-07-15 17:02:59 +020026import org.onosproject.core.ApplicationId;
Andrea Campanellae3708782017-10-16 16:00:21 +020027import org.onosproject.mastership.MastershipService;
Jian Li1932b932016-01-03 00:35:40 -080028import org.onosproject.net.DeviceId;
Andrea Campanella32a9c0b2020-03-27 12:53:46 +010029import org.onosproject.net.config.NetworkConfigRegistry;
30import org.onosproject.net.config.basics.BasicDeviceConfig;
Gamze Abakaf57ef602019-03-11 06:52:48 +000031import org.onosproject.net.device.DeviceEvent;
32import org.onosproject.net.device.DeviceListener;
Andrea Campanellae3708782017-10-16 16:00:21 +020033import org.onosproject.net.device.DeviceService;
34import org.onosproject.net.driver.DriverService;
alshabib10c810b2015-08-18 16:59:04 -070035import org.onosproject.net.meter.DefaultMeter;
36import org.onosproject.net.meter.Meter;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070037import org.onosproject.net.meter.MeterCellId.MeterCellType;
alshabib10c810b2015-08-18 16:59:04 -070038import org.onosproject.net.meter.MeterEvent;
39import org.onosproject.net.meter.MeterFailReason;
Jordi Ortizaa8de492016-12-01 00:21:36 +010040import org.onosproject.net.meter.MeterFeatures;
alshabib10c810b2015-08-18 16:59:04 -070041import org.onosproject.net.meter.MeterId;
alshabib70aaa1b2015-09-25 14:30:59 -070042import org.onosproject.net.meter.MeterKey;
alshabib10c810b2015-08-18 16:59:04 -070043import org.onosproject.net.meter.MeterListener;
44import org.onosproject.net.meter.MeterOperation;
45import org.onosproject.net.meter.MeterProvider;
46import org.onosproject.net.meter.MeterProviderRegistry;
47import org.onosproject.net.meter.MeterProviderService;
alshabibe1248b62015-08-20 17:21:55 -070048import org.onosproject.net.meter.MeterRequest;
alshabib10c810b2015-08-18 16:59:04 -070049import org.onosproject.net.meter.MeterService;
50import org.onosproject.net.meter.MeterState;
51import org.onosproject.net.meter.MeterStore;
52import org.onosproject.net.meter.MeterStoreDelegate;
53import org.onosproject.net.meter.MeterStoreResult;
alshabib1d2bc402015-07-31 17:04:11 -070054import org.onosproject.net.provider.AbstractListenerProviderRegistry;
55import org.onosproject.net.provider.AbstractProviderService;
Andrea Campanellae3708782017-10-16 16:00:21 +020056import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070057import org.osgi.service.component.annotations.Activate;
58import org.osgi.service.component.annotations.Component;
59import org.osgi.service.component.annotations.Deactivate;
60import org.osgi.service.component.annotations.Modified;
61import org.osgi.service.component.annotations.Reference;
62import org.osgi.service.component.annotations.ReferenceCardinality;
alshabib1d2bc402015-07-31 17:04:11 -070063import org.slf4j.Logger;
64
65import java.util.Collection;
Andrea Campanellae3708782017-10-16 16:00:21 +020066import java.util.Dictionary;
alshabib5eb79392015-08-19 18:09:55 -070067import java.util.Map;
pierventre1b8afbc2020-07-13 14:07:05 +020068import java.util.Objects;
alshabib5eb79392015-08-19 18:09:55 -070069import java.util.stream.Collectors;
alshabib1d2bc402015-07-31 17:04:11 -070070
Jordi Ortiz31d4d382017-07-19 10:52:26 +020071import static com.google.common.base.Preconditions.checkNotNull;
Andrea Campanellae3708782017-10-16 16:00:21 +020072import static com.google.common.base.Strings.isNullOrEmpty;
pierventre1b8afbc2020-07-13 14:07:05 +020073import static org.onlab.util.PredictableExecutor.newPredictableExecutor;
Andrea Campanellae3708782017-10-16 16:00:21 +020074import static org.onlab.util.Tools.get;
Jordi Ortiz31d4d382017-07-19 10:52:26 +020075import static org.onlab.util.Tools.groupedThreads;
Ray Milkeyd04e2272018-10-16 18:20:18 -070076import static org.onosproject.net.OsgiPropertyConstants.MM_FALLBACK_METER_POLL_FREQUENCY;
77import static org.onosproject.net.OsgiPropertyConstants.MM_FALLBACK_METER_POLL_FREQUENCY_DEFAULT;
78import static org.onosproject.net.OsgiPropertyConstants.MM_NUM_THREADS;
79import static org.onosproject.net.OsgiPropertyConstants.MM_NUM_THREADS_DEFAULT;
Gamze Abakaf57ef602019-03-11 06:52:48 +000080import static org.onosproject.net.OsgiPropertyConstants.MM_PURGE_ON_DISCONNECTION;
81import static org.onosproject.net.OsgiPropertyConstants.MM_PURGE_ON_DISCONNECTION_DEFAULT;
alshabib1d2bc402015-07-31 17:04:11 -070082import static org.slf4j.LoggerFactory.getLogger;
83
alshabib1d2bc402015-07-31 17:04:11 -070084/**
85 * Provides implementation of the meter service APIs.
86 */
Ray Milkeyd04e2272018-10-16 18:20:18 -070087@Component(
Gamze Abakaf57ef602019-03-11 06:52:48 +000088 immediate = true,
89 service = {
90 MeterService.class,
91 MeterProviderRegistry.class
92 },
93 property = {
94 MM_NUM_THREADS + ":Integer=" + MM_NUM_THREADS_DEFAULT,
95 MM_FALLBACK_METER_POLL_FREQUENCY + ":Integer=" + MM_FALLBACK_METER_POLL_FREQUENCY_DEFAULT,
96 MM_PURGE_ON_DISCONNECTION + ":Boolean=" + MM_PURGE_ON_DISCONNECTION_DEFAULT,
97 }
Ray Milkeyd04e2272018-10-16 18:20:18 -070098)
Thomas Vachuska11b99fc2017-04-27 12:51:04 -070099public class MeterManager
100 extends AbstractListenerProviderRegistry<MeterEvent, MeterListener, MeterProvider, MeterProviderService>
alshabib1d2bc402015-07-31 17:04:11 -0700101 implements MeterService, MeterProviderRegistry {
pierventre1b8afbc2020-07-13 14:07:05 +0200102 // Installer related objects
103 private PredictableExecutor meterInstallers;
Jordi Ortiz31d4d382017-07-19 10:52:26 +0200104 private static final String WORKER_PATTERN = "installer-%d";
105 private static final String GROUP_THREAD_NAME = "onos/meter";
pierventre1b8afbc2020-07-13 14:07:05 +0200106 // Logging facility, meter store delegate and listener for device events.
alshabib1d2bc402015-07-31 17:04:11 -0700107 private final Logger log = getLogger(getClass());
108 private final MeterStoreDelegate delegate = new InternalMeterStoreDelegate();
Gamze Abakaf57ef602019-03-11 06:52:48 +0000109 private final DeviceListener deviceListener = new InternalDeviceListener();
alshabib1d2bc402015-07-31 17:04:11 -0700110
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700111 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Pier Luigif094c612017-10-14 12:15:02 +0200112 private MeterStore store;
alshabib1d2bc402015-07-31 17:04:11 -0700113
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700114 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanellae3708782017-10-16 16:00:21 +0200115 protected DriverService driverService;
116
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700117 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanellae3708782017-10-16 16:00:21 +0200118 protected DeviceService deviceService;
119
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700120 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanellae3708782017-10-16 16:00:21 +0200121 protected ComponentConfigService cfgService;
122
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700123 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanellae3708782017-10-16 16:00:21 +0200124 protected MastershipService mastershipService;
125
Andrea Campanella32a9c0b2020-03-27 12:53:46 +0100126 @Reference(cardinality = ReferenceCardinality.MANDATORY)
127 protected NetworkConfigRegistry netCfgService;
128
pierventre1b8afbc2020-07-13 14:07:05 +0200129 @Reference(cardinality = ReferenceCardinality.MANDATORY)
130 protected ClusterService clusterService;
131
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700132 /** Number of worker threads. */
pierventre1b8afbc2020-07-13 14:07:05 +0200133 // TODO Set 0 to use the available processors
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700134 private int numThreads = MM_NUM_THREADS_DEFAULT;
135
136 /** Frequency (in seconds) for polling meters via fallback provider. */
Ray Milkeyd04e2272018-10-16 18:20:18 -0700137 private int fallbackMeterPollFrequency = MM_FALLBACK_METER_POLL_FREQUENCY_DEFAULT;
Andrea Campanellae3708782017-10-16 16:00:21 +0200138
Gamze Abakaf57ef602019-03-11 06:52:48 +0000139 /** Purge entries associated with a device when the device goes offline. */
140 private boolean purgeOnDisconnection = MM_PURGE_ON_DISCONNECTION_DEFAULT;
141
pierventre1b8afbc2020-07-13 14:07:05 +0200142 // Action triggered when the futures related to submit and withdrawal complete
alshabibe1248b62015-08-20 17:21:55 -0700143 private TriConsumer<MeterRequest, MeterStoreResult, Throwable> onComplete;
alshabibeadfc8e2015-08-18 15:40:46 -0700144
pierventre1b8afbc2020-07-13 14:07:05 +0200145 // Meter provider reference
Andrea Campanellae3708782017-10-16 16:00:21 +0200146 private final MeterDriverProvider defaultProvider = new MeterDriverProvider();
147
pierventre1b8afbc2020-07-13 14:07:05 +0200148 // Node id used to verify who is charge of the meter ops
149 // (usually one node can modify the internal state of the device)
150 private NodeId local;
151
alshabib1d2bc402015-07-31 17:04:11 -0700152 @Activate
Andrea Campanellae3708782017-10-16 16:00:21 +0200153 public void activate(ComponentContext context) {
alshabib58fe6dc2015-08-19 17:16:13 -0700154 store.setDelegate(delegate);
Andrea Campanellae3708782017-10-16 16:00:21 +0200155 cfgService.registerProperties(getClass());
Jordi Ortiz3a6f3072016-11-07 10:23:48 +0100156 eventDispatcher.addSink(MeterEvent.class, listenerRegistry);
Gamze Abakaf57ef602019-03-11 06:52:48 +0000157 deviceService.addListener(deviceListener);
pierventre1b8afbc2020-07-13 14:07:05 +0200158 local = clusterService.getLocalNode().id();
159 // Consumer logic is the following:
160 // if there is an exceptional end (storage exception), on error is called
161 // else if there is a reason for the failure, on error is called with the reason
162 // else if the reason is empty, on success is called
163 // In all the cases the meter context code is consumed
Jordi Ortiz3a6f3072016-11-07 10:23:48 +0100164 onComplete = (request, result, error) -> {
alshabibe1248b62015-08-20 17:21:55 -0700165 request.context().ifPresent(c -> {
alshabibeadfc8e2015-08-18 15:40:46 -0700166 if (error != null) {
alshabibe1248b62015-08-20 17:21:55 -0700167 c.onError(request, MeterFailReason.UNKNOWN);
alshabibeadfc8e2015-08-18 15:40:46 -0700168 } else {
169 if (result.reason().isPresent()) {
alshabibe1248b62015-08-20 17:21:55 -0700170 c.onError(request, result.reason().get());
alshabibeadfc8e2015-08-18 15:40:46 -0700171 } else {
alshabibe1248b62015-08-20 17:21:55 -0700172 c.onSuccess(request);
alshabibeadfc8e2015-08-18 15:40:46 -0700173 }
174 }
175 });
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900176 };
Jordi Ortiz31d4d382017-07-19 10:52:26 +0200177
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700178 modified(context);
alshabib1d2bc402015-07-31 17:04:11 -0700179 log.info("Started");
180 }
181
Andrea Campanellae3708782017-10-16 16:00:21 +0200182 @Modified
183 public void modified(ComponentContext context) {
184 if (context != null) {
185 readComponentConfiguration(context);
186 }
187 defaultProvider.init(deviceService, createProviderService(defaultProvider),
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900188 mastershipService, fallbackMeterPollFrequency);
Andrea Campanellae3708782017-10-16 16:00:21 +0200189 }
190
alshabib1d2bc402015-07-31 17:04:11 -0700191 @Deactivate
192 public void deactivate() {
Andrea Campanellae3708782017-10-16 16:00:21 +0200193 defaultProvider.terminate();
alshabib58fe6dc2015-08-19 17:16:13 -0700194 store.unsetDelegate(delegate);
Thomas Vachuska11b99fc2017-04-27 12:51:04 -0700195 eventDispatcher.removeSink(MeterEvent.class);
Gamze Abakaf57ef602019-03-11 06:52:48 +0000196 deviceService.removeListener(deviceListener);
Andrea Campanellae3708782017-10-16 16:00:21 +0200197 cfgService.unregisterProperties(getClass(), false);
pierventre1b8afbc2020-07-13 14:07:05 +0200198 meterInstallers.shutdown();
alshabib1d2bc402015-07-31 17:04:11 -0700199 log.info("Stopped");
200 }
201
Andrea Campanellae3708782017-10-16 16:00:21 +0200202 /**
203 * Extracts properties from the component configuration context.
204 *
205 * @param context the component context
206 */
207 private void readComponentConfiguration(ComponentContext context) {
208 Dictionary<?, ?> properties = context.getProperties();
Gamze Abakaf57ef602019-03-11 06:52:48 +0000209 Boolean flag;
210
211 flag = Tools.isPropertyEnabled(properties, MM_PURGE_ON_DISCONNECTION);
212 if (flag == null) {
213 log.info("PurgeOnDisconnection is not configured," +
Andrea Campanella32a9c0b2020-03-27 12:53:46 +0100214 "using current value of {}", purgeOnDisconnection);
Gamze Abakaf57ef602019-03-11 06:52:48 +0000215 } else {
216 purgeOnDisconnection = flag;
217 log.info("Configured. PurgeOnDisconnection is {}",
Andrea Campanella32a9c0b2020-03-27 12:53:46 +0100218 purgeOnDisconnection ? "enabled" : "disabled");
Gamze Abakaf57ef602019-03-11 06:52:48 +0000219 }
Andrea Campanellae3708782017-10-16 16:00:21 +0200220
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700221 String s = get(properties, MM_FALLBACK_METER_POLL_FREQUENCY);
Andrea Campanellae3708782017-10-16 16:00:21 +0200222 try {
Ray Milkeyd04e2272018-10-16 18:20:18 -0700223 fallbackMeterPollFrequency = isNullOrEmpty(s) ?
224 MM_FALLBACK_METER_POLL_FREQUENCY_DEFAULT : Integer.parseInt(s);
Andrea Campanellae3708782017-10-16 16:00:21 +0200225 } catch (NumberFormatException e) {
Ray Milkeyd04e2272018-10-16 18:20:18 -0700226 fallbackMeterPollFrequency = MM_FALLBACK_METER_POLL_FREQUENCY_DEFAULT;
Andrea Campanellae3708782017-10-16 16:00:21 +0200227 }
Thomas Vachuskaf566fa22018-10-30 14:03:36 -0700228
229 s = get(properties, MM_NUM_THREADS);
230 try {
231 numThreads = isNullOrEmpty(s) ? MM_NUM_THREADS_DEFAULT : Integer.parseInt(s);
232 } catch (NumberFormatException e) {
233 numThreads = MM_NUM_THREADS_DEFAULT;
234 }
pierventre1b8afbc2020-07-13 14:07:05 +0200235 if (meterInstallers != null) {
236 meterInstallers.shutdown();
237 }
238 meterInstallers = newPredictableExecutor(numThreads,
239 groupedThreads(GROUP_THREAD_NAME, WORKER_PATTERN, log));
Andrea Campanellae3708782017-10-16 16:00:21 +0200240 }
241
242 @Override
243 protected MeterProvider defaultProvider() {
244 return defaultProvider;
245 }
246
alshabib1d2bc402015-07-31 17:04:11 -0700247 @Override
248 protected MeterProviderService createProviderService(MeterProvider provider) {
249 return new InternalMeterProviderService(provider);
250 }
251
252 @Override
alshabibe1248b62015-08-20 17:21:55 -0700253 public Meter submit(MeterRequest request) {
柯志勇10068695d709dba2018-10-23 18:35:49 +0800254 checkNotNull(request, "request cannot be null.");
pierventre1b8afbc2020-07-13 14:07:05 +0200255 // Allocate an id and then submit the request
alshabib70aaa1b2015-09-25 14:30:59 -0700256 MeterId id = allocateMeterId(request.deviceId());
alshabibe1248b62015-08-20 17:21:55 -0700257 Meter.Builder mBuilder = DefaultMeter.builder()
258 .forDevice(request.deviceId())
259 .fromApp(request.appId())
260 .withBands(request.bands())
Gamze Abaka65f27f12018-07-09 06:40:24 +0000261 .withCellId(id)
alshabibe1248b62015-08-20 17:21:55 -0700262 .withUnit(request.unit());
alshabibe1248b62015-08-20 17:21:55 -0700263 if (request.isBurst()) {
264 mBuilder.burst();
265 }
Andrea Campanella5bdbe432021-05-03 15:59:19 +0200266 if (request.annotations() != null && !request.annotations().keys().isEmpty()) {
267 mBuilder.withAnnotations(request.annotations());
268 }
alshabibe1248b62015-08-20 17:21:55 -0700269 DefaultMeter m = (DefaultMeter) mBuilder.build();
pierventre1b8afbc2020-07-13 14:07:05 +0200270 // Meter installation logic (happy ending case)
271 // PENDING -> stats -> ADDED -> future completes
alshabib7bb05012015-08-05 10:15:09 -0700272 m.setState(MeterState.PENDING_ADD);
alshabibeadfc8e2015-08-18 15:40:46 -0700273 store.storeMeter(m).whenComplete((result, error) ->
alshabibe1248b62015-08-20 17:21:55 -0700274 onComplete.accept(request, result, error));
275 return m;
alshabib1d2bc402015-07-31 17:04:11 -0700276 }
277
278 @Override
alshabibe1248b62015-08-20 17:21:55 -0700279 public void withdraw(MeterRequest request, MeterId meterId) {
柯志勇10068695d709dba2018-10-23 18:35:49 +0800280 checkNotNull(request, "request cannot be null.");
alshabibe1248b62015-08-20 17:21:55 -0700281 Meter.Builder mBuilder = DefaultMeter.builder()
282 .forDevice(request.deviceId())
283 .fromApp(request.appId())
284 .withBands(request.bands())
Gamze Abaka65f27f12018-07-09 06:40:24 +0000285 .withCellId(meterId)
alshabibe1248b62015-08-20 17:21:55 -0700286 .withUnit(request.unit());
alshabib1d2bc402015-07-31 17:04:11 -0700287
alshabibe1248b62015-08-20 17:21:55 -0700288 if (request.isBurst()) {
289 mBuilder.burst();
290 }
alshabibe1248b62015-08-20 17:21:55 -0700291 DefaultMeter m = (DefaultMeter) mBuilder.build();
pierventre1b8afbc2020-07-13 14:07:05 +0200292 // Meter removal logic (happy ending case)
293 // PENDING -> stats -> removed from the map -> future completes
294 // There is no transition to the REMOVED state
alshabib7bb05012015-08-05 10:15:09 -0700295 m.setState(MeterState.PENDING_REMOVE);
alshabibeadfc8e2015-08-18 15:40:46 -0700296 store.deleteMeter(m).whenComplete((result, error) ->
alshabibe1248b62015-08-20 17:21:55 -0700297 onComplete.accept(request, result, error));
alshabib1d2bc402015-07-31 17:04:11 -0700298 }
299
300 @Override
alshabib70aaa1b2015-09-25 14:30:59 -0700301 public Meter getMeter(DeviceId deviceId, MeterId id) {
302 MeterKey key = MeterKey.key(deviceId, id);
303 return store.getMeter(key);
alshabib1d2bc402015-07-31 17:04:11 -0700304 }
305
306 @Override
Jian Li1932b932016-01-03 00:35:40 -0800307 public Collection<Meter> getMeters(DeviceId deviceId) {
308 return store.getAllMeters().stream().filter(m ->
309 m.deviceId().equals(deviceId)).collect(Collectors.toList());
310 }
311
312 @Override
alshabib58fe6dc2015-08-19 17:16:13 -0700313 public Collection<Meter> getAllMeters() {
314 return store.getAllMeters();
315 }
316
Pier Luigibdcd9672017-10-13 13:54:48 +0200317 @Override
318 public MeterId allocateMeterId(DeviceId deviceId) {
pierventre1b8afbc2020-07-13 14:07:05 +0200319 // We delegate directly to the store
Pier Luigif094c612017-10-14 12:15:02 +0200320 return store.allocateMeterId(deviceId);
alshabib1d2bc402015-07-31 17:04:11 -0700321 }
322
Pier Luigibdcd9672017-10-13 13:54:48 +0200323 @Override
324 public void freeMeterId(DeviceId deviceId, MeterId meterId) {
pierventre1b8afbc2020-07-13 14:07:05 +0200325 // We delegate directly to the store
Pier Luigibdcd9672017-10-13 13:54:48 +0200326 store.freeMeterId(deviceId, meterId);
327 }
328
Andrea Campanella23250502020-05-13 15:36:57 +0200329 @Override
330 public void purgeMeters(DeviceId deviceId) {
pierventre1b8afbc2020-07-13 14:07:05 +0200331 // We delegate directly to the store
Andrea Campanella23250502020-05-13 15:36:57 +0200332 store.purgeMeter(deviceId);
333 }
334
Daniele Moro43ac2892021-07-15 17:02:59 +0200335 @Override
336 public void purgeMeters(DeviceId deviceId, ApplicationId appId) {
337 store.purgeMeters(deviceId, appId);
338 }
339
alshabib1d2bc402015-07-31 17:04:11 -0700340 private class InternalMeterProviderService
341 extends AbstractProviderService<MeterProvider>
342 implements MeterProviderService {
343
344 /**
345 * Creates a provider service on behalf of the specified provider.
346 *
347 * @param provider provider to which this service is being issued
348 */
349 protected InternalMeterProviderService(MeterProvider provider) {
350 super(provider);
351 }
352
353 @Override
alshabib7bb05012015-08-05 10:15:09 -0700354 public void meterOperationFailed(MeterOperation operation,
355 MeterFailReason reason) {
356 store.failedMeter(operation, reason);
alshabib1d2bc402015-07-31 17:04:11 -0700357 }
358
359 @Override
360 public void pushMeterMetrics(DeviceId deviceId, Collection<Meter> meterEntries) {
pierventre1b8afbc2020-07-13 14:07:05 +0200361 // Each update on the store is reflected on this collection
Jordi Ortiz9287b632017-06-22 11:01:37 +0200362 Collection<Meter> allMeters = store.getAllMeters(deviceId);
alshabib5eb79392015-08-19 18:09:55 -0700363
Jordi Ortiz58896912017-03-06 14:51:05 +0100364 Map<MeterId, Meter> meterEntriesMap = meterEntries.stream()
365 .collect(Collectors.toMap(Meter::id, Meter -> Meter));
366
Jordi Ortiz205af452017-06-22 14:25:04 +0200367 // Look for meters defined in onos and missing in the device (restore)
Jordi Ortiz9287b632017-06-22 11:01:37 +0200368 allMeters.stream().forEach(m -> {
369 if ((m.state().equals(MeterState.PENDING_ADD) ||
370 m.state().equals(MeterState.ADDED)) &&
371 !meterEntriesMap.containsKey(m.id())) {
Jordi Ortiz58896912017-03-06 14:51:05 +0100372 // The meter is missing in the device. Reinstall!
Jordi Ortiz205af452017-06-22 14:25:04 +0200373 log.debug("Adding meter missing in device {} {}", deviceId, m);
pierventre1b8afbc2020-07-13 14:07:05 +0200374 // offload the task to avoid the overloading of the sb threads
375 meterInstallers.execute(new MeterInstaller(deviceId, m, MeterOperation.Type.ADD));
Jordi Ortiz58896912017-03-06 14:51:05 +0100376 }
Jordi Ortiz58896912017-03-06 14:51:05 +0100377 });
378
Jordi Ortiz205af452017-06-22 14:25:04 +0200379 // Look for meters defined in the device and not in onos (remove)
380 meterEntriesMap.entrySet().stream()
381 .filter(md -> !allMeters.stream().anyMatch(m -> m.id().equals(md.getKey())))
382 .forEach(mio -> {
Jordi Ortiz205af452017-06-22 14:25:04 +0200383 Meter meter = mio.getValue();
pierventre1b8afbc2020-07-13 14:07:05 +0200384 // FIXME: Removing a meter is meaningful for OpenFlow, but not for P4Runtime.
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900385 // In P4Runtime meter cells cannot be removed. For the
386 // moment, we make the distinction between OpenFlow and
387 // P4Runtime by looking at the MeterCellType (always
388 // INDEX for OpenFlow).
389 if (meter.meterCellId().type() == MeterCellType.INDEX) {
390 // The meter is missing in onos. Uninstall!
391 log.debug("Remove meter in device not in onos {} {}", deviceId, mio.getKey());
pierventre1b8afbc2020-07-13 14:07:05 +0200392 // offload the task to avoid the overloading of the sb threads
393 meterInstallers.execute(new MeterInstaller(deviceId, meter, MeterOperation.Type.REMOVE));
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900394 }
395 });
Jordi Ortiz205af452017-06-22 14:25:04 +0200396
pierventre1b8afbc2020-07-13 14:07:05 +0200397 // Update the meter stats in the store (first time move the state from pending to added)
pierventre44220052020-09-22 12:51:06 +0200398 Collection<Meter> addedMeters = Sets.newHashSet();
alshabib5eb79392015-08-19 18:09:55 -0700399 meterEntries.stream()
Jordi Ortiz9287b632017-06-22 11:01:37 +0200400 .filter(m -> allMeters.stream()
401 .anyMatch(sm -> sm.deviceId().equals(deviceId) && sm.id().equals(m.id())))
pierventre44220052020-09-22 12:51:06 +0200402 .forEach(m -> {
403 Meter updatedMeter = store.updateMeterState(m);
404 if (updatedMeter != null && updatedMeter.state() == MeterState.ADDED) {
405 addedMeters.add(updatedMeter);
406 }
407 });
408 Collection<Meter> newAllMeters = Sets.newHashSet(allMeters);
409 newAllMeters.removeAll(addedMeters);
alshabib5eb79392015-08-19 18:09:55 -0700410
pierventre44220052020-09-22 12:51:06 +0200411 newAllMeters.forEach(m -> {
pierventre1b8afbc2020-07-13 14:07:05 +0200412 // FIXME: Installing a meter is meaningful for OpenFlow, but not for P4Runtime.
413 // It looks like this flow is used only for p4runtime to emulate the installation
414 // since meters are already instantiated - we need just modify the params.
Saurav Das381c7ca2020-09-21 15:26:50 -0700415 if (m.state() == MeterState.PENDING_ADD && m.meterCellId().type() != MeterCellType.INDEX) {
pierventre1b8afbc2020-07-13 14:07:05 +0200416 // offload the task to avoid the overloading of the sb threads
pierventre44220052020-09-22 12:51:06 +0200417 log.debug("Modify meter {} in device {}", m.id(), deviceId);
pierventre1b8afbc2020-07-13 14:07:05 +0200418 meterInstallers.execute(new MeterInstaller(m.deviceId(), m, MeterOperation.Type.MODIFY));
419 // Remove workflow. Regarding OpenFlow, meters have been removed from
420 // the device but they are still in the store, we will purge them definitely.
421 // Instead, P4Runtime devices will not remove the meter. The first workaround
422 // for P4Runtime will avoid to send a remove op. Then, we reach this point
423 // and we purge the meter from the store
Konstantinos Kanonakisa45755d2016-03-14 11:31:23 -0500424 } else if (m.state() == MeterState.PENDING_REMOVE) {
pierventre44220052020-09-22 12:51:06 +0200425 log.debug("Delete meter {} now in store", m.id());
Wailok Shumf013a782021-07-26 16:51:01 +0800426 store.purgeMeter(m);
alshabib5eb79392015-08-19 18:09:55 -0700427 }
428 });
alshabib1d2bc402015-07-31 17:04:11 -0700429 }
Jordi Ortizaa8de492016-12-01 00:21:36 +0100430
431 @Override
432 public void pushMeterFeatures(DeviceId deviceId, MeterFeatures meterfeatures) {
433 store.storeMeterFeatures(meterfeatures);
434 }
435
436 @Override
Wailok Shumf013a782021-07-26 16:51:01 +0800437 public void pushMeterFeatures(DeviceId deviceId, Collection<MeterFeatures> meterfeatures) {
438 meterfeatures.forEach(mf -> store.storeMeterFeatures(mf));
439 }
440
441 @Override
Jordi Ortizaa8de492016-12-01 00:21:36 +0100442 public void deleteMeterFeatures(DeviceId deviceId) {
443 store.deleteMeterFeatures(deviceId);
444 }
alshabib1d2bc402015-07-31 17:04:11 -0700445 }
446
447 private class InternalMeterStoreDelegate implements MeterStoreDelegate {
448
449 @Override
450 public void notify(MeterEvent event) {
alshabibeadfc8e2015-08-18 15:40:46 -0700451 DeviceId deviceId = event.subject().deviceId();
alshabib7bb05012015-08-05 10:15:09 -0700452 switch (event.type()) {
pierventre1b8afbc2020-07-13 14:07:05 +0200453 // REQ events will trigger a modification in the device.
454 // Mastership check is performed inside the installer
455 // to avoid the blocking of the RAFT threads
alshabibeadfc8e2015-08-18 15:40:46 -0700456 case METER_ADD_REQ:
pierventre1b8afbc2020-07-13 14:07:05 +0200457 meterInstallers.execute(new MeterInstaller(deviceId, event.subject(),
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900458 MeterOperation.Type.ADD));
alshabib7bb05012015-08-05 10:15:09 -0700459 break;
alshabibeadfc8e2015-08-18 15:40:46 -0700460 case METER_REM_REQ:
pierventre1b8afbc2020-07-13 14:07:05 +0200461 meterInstallers.execute(new MeterInstaller(deviceId, event.subject(),
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900462 MeterOperation.Type.REMOVE));
alshabib7bb05012015-08-05 10:15:09 -0700463 break;
pierventre1b8afbc2020-07-13 14:07:05 +0200464 // Following events are forwarded to the apps subscribed for the meter events;
465 // installers are not involved in this task. In this case, the overhead for this op
466 // is almost null. Potentially we can introduce a store delegate thread.
Jordi Ortizdf28ecd2017-03-25 19:22:36 +0100467 case METER_ADDED:
Jordi Ortizdf28ecd2017-03-25 19:22:36 +0100468 case METER_REMOVED:
Gamze Abakadadae722018-09-12 10:55:35 +0000469 case METER_REFERENCE_COUNT_ZERO:
pierventre1b8afbc2020-07-13 14:07:05 +0200470 log.debug("Post {} event {}", event.type(), event.subject());
471 post(event);
Gamze Abakadadae722018-09-12 10:55:35 +0000472 break;
alshabib7bb05012015-08-05 10:15:09 -0700473 default:
474 log.warn("Unknown meter event {}", event.type());
475 }
alshabib1d2bc402015-07-31 17:04:11 -0700476
477 }
478 }
pierventre1b8afbc2020-07-13 14:07:05 +0200479
Jordi Ortiz31d4d382017-07-19 10:52:26 +0200480 /**
481 * Task that passes the meter down to the provider.
482 */
pierventre1b8afbc2020-07-13 14:07:05 +0200483 private class MeterInstaller implements PickyRunnable {
Jordi Ortiz31d4d382017-07-19 10:52:26 +0200484 private final DeviceId deviceId;
485 private final Meter meter;
486 private final MeterOperation.Type op;
487
488 public MeterInstaller(DeviceId deviceId, Meter meter, MeterOperation.Type op) {
489 this.deviceId = checkNotNull(deviceId);
490 this.meter = checkNotNull(meter);
491 this.op = checkNotNull(op);
492 }
493
494 @Override
495 public void run() {
pierventre1b8afbc2020-07-13 14:07:05 +0200496 // Check mastership and eventually execute the op on the device
497 log.debug("Meter {} request {}", op.name().toLowerCase(), meter);
498 NodeId master = mastershipService.getMasterFor(meter.deviceId());
499 if (!Objects.equals(local, master)) {
500 log.trace("Not the master of device {}, skipping installation of the meter {}",
501 meter.deviceId(), meter.id());
502 return;
503 }
Jordi Ortiz31d4d382017-07-19 10:52:26 +0200504 MeterProvider p = getProvider(this.deviceId);
505 if (p == null) {
506 log.error("Unable to recover {}'s provider", deviceId);
507 return;
508 }
509 p.performMeterOperation(deviceId, new MeterOperation(meter, op));
510 }
pierventre1b8afbc2020-07-13 14:07:05 +0200511
512 @Override
513 public int hint() {
514 return meter.id().hashCode();
515 }
Jordi Ortiz31d4d382017-07-19 10:52:26 +0200516 }
alshabib1d2bc402015-07-31 17:04:11 -0700517
Gamze Abakaf57ef602019-03-11 06:52:48 +0000518 private class InternalDeviceListener implements DeviceListener {
519
520 @Override
521 public void event(DeviceEvent event) {
pierventre52ef9332021-07-09 22:42:17 +0200522 DeviceId deviceId = event.subject().id();
523 meterInstallers.execute(() -> {
524 switch (event.type()) {
525 case DEVICE_REMOVED:
526 case DEVICE_AVAILABILITY_CHANGED:
527 if (!deviceService.isAvailable(deviceId)) {
528 BasicDeviceConfig cfg = netCfgService.getConfig(deviceId, BasicDeviceConfig.class);
529 //if purgeOnDisconnection is set for the device or it's a global configuration
530 // lets remove the meters.
531 boolean purge = cfg != null && cfg.isPurgeOnDisconnectionConfigured() ?
532 cfg.purgeOnDisconnection() : purgeOnDisconnection;
533 if (purge) {
534 log.info("PurgeOnDisconnection is requested for device {}, " +
535 "removing meters", deviceId);
536 store.purgeMeter(deviceId);
537 }
Gamze Abakaf57ef602019-03-11 06:52:48 +0000538 }
pierventre52ef9332021-07-09 22:42:17 +0200539 break;
540 default:
541 break;
542 }
543 }, deviceId.hashCode());
544
Gamze Abakaf57ef602019-03-11 06:52:48 +0000545 }
546 }
547
alshabib1d2bc402015-07-31 17:04:11 -0700548}