blob: f39b328f83c4acbcc92ed58a7020bdd3cfd8e3d5 [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
18import org.apache.felix.scr.annotations.Activate;
19import org.apache.felix.scr.annotations.Component;
20import org.apache.felix.scr.annotations.Deactivate;
Andrea Campanellae3708782017-10-16 16:00:21 +020021import org.apache.felix.scr.annotations.Modified;
Jordi Ortiz31d4d382017-07-19 10:52:26 +020022import org.apache.felix.scr.annotations.Property;
alshabib1d2bc402015-07-31 17:04:11 -070023import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
25import org.apache.felix.scr.annotations.Service;
alshabibeadfc8e2015-08-18 15:40:46 -070026import org.onlab.util.TriConsumer;
Andrea Campanellae3708782017-10-16 16:00:21 +020027import org.onosproject.cfg.ComponentConfigService;
28import org.onosproject.mastership.MastershipService;
Jian Li1932b932016-01-03 00:35:40 -080029import org.onosproject.net.DeviceId;
Andrea Campanellae3708782017-10-16 16:00:21 +020030import org.onosproject.net.device.DeviceService;
31import org.onosproject.net.driver.DriverService;
alshabib10c810b2015-08-18 16:59:04 -070032import org.onosproject.net.meter.DefaultMeter;
Frank Wangd7e3b4b2017-09-24 13:37:54 +090033import org.onosproject.net.meter.MeterCellId.MeterCellType;
alshabib10c810b2015-08-18 16:59:04 -070034import org.onosproject.net.meter.Meter;
35import org.onosproject.net.meter.MeterEvent;
36import org.onosproject.net.meter.MeterFailReason;
Jordi Ortizaa8de492016-12-01 00:21:36 +010037import org.onosproject.net.meter.MeterFeatures;
alshabib10c810b2015-08-18 16:59:04 -070038import org.onosproject.net.meter.MeterId;
alshabib70aaa1b2015-09-25 14:30:59 -070039import org.onosproject.net.meter.MeterKey;
alshabib10c810b2015-08-18 16:59:04 -070040import org.onosproject.net.meter.MeterListener;
41import org.onosproject.net.meter.MeterOperation;
42import org.onosproject.net.meter.MeterProvider;
43import org.onosproject.net.meter.MeterProviderRegistry;
44import org.onosproject.net.meter.MeterProviderService;
alshabibe1248b62015-08-20 17:21:55 -070045import org.onosproject.net.meter.MeterRequest;
alshabib10c810b2015-08-18 16:59:04 -070046import org.onosproject.net.meter.MeterService;
47import org.onosproject.net.meter.MeterState;
48import org.onosproject.net.meter.MeterStore;
49import org.onosproject.net.meter.MeterStoreDelegate;
50import org.onosproject.net.meter.MeterStoreResult;
alshabib1d2bc402015-07-31 17:04:11 -070051import org.onosproject.net.provider.AbstractListenerProviderRegistry;
52import org.onosproject.net.provider.AbstractProviderService;
Andrea Campanellae3708782017-10-16 16:00:21 +020053import org.osgi.service.component.ComponentContext;
alshabib1d2bc402015-07-31 17:04:11 -070054import org.slf4j.Logger;
55
56import java.util.Collection;
Andrea Campanellae3708782017-10-16 16:00:21 +020057import java.util.Dictionary;
alshabib5eb79392015-08-19 18:09:55 -070058import java.util.Map;
Jordi Ortiz31d4d382017-07-19 10:52:26 +020059import java.util.concurrent.ExecutorService;
alshabib5eb79392015-08-19 18:09:55 -070060import java.util.stream.Collectors;
alshabib1d2bc402015-07-31 17:04:11 -070061
Jordi Ortiz31d4d382017-07-19 10:52:26 +020062import static com.google.common.base.Preconditions.checkNotNull;
Andrea Campanellae3708782017-10-16 16:00:21 +020063import static com.google.common.base.Strings.isNullOrEmpty;
Jordi Ortiz31d4d382017-07-19 10:52:26 +020064import static java.util.concurrent.Executors.newFixedThreadPool;
Andrea Campanellae3708782017-10-16 16:00:21 +020065import static org.onlab.util.Tools.get;
Jordi Ortiz31d4d382017-07-19 10:52:26 +020066import static org.onlab.util.Tools.groupedThreads;
alshabib1d2bc402015-07-31 17:04:11 -070067import static org.slf4j.LoggerFactory.getLogger;
68
alshabib1d2bc402015-07-31 17:04:11 -070069/**
70 * Provides implementation of the meter service APIs.
71 */
Thomas Vachuska11b99fc2017-04-27 12:51:04 -070072@Component(immediate = true)
alshabib1d2bc402015-07-31 17:04:11 -070073@Service
Thomas Vachuska11b99fc2017-04-27 12:51:04 -070074public class MeterManager
75 extends AbstractListenerProviderRegistry<MeterEvent, MeterListener, MeterProvider, MeterProviderService>
alshabib1d2bc402015-07-31 17:04:11 -070076 implements MeterService, MeterProviderRegistry {
77
Jordi Ortiz31d4d382017-07-19 10:52:26 +020078 private static final String NUM_THREAD = "numThreads";
79 private static final String WORKER_PATTERN = "installer-%d";
80 private static final String GROUP_THREAD_NAME = "onos/meter";
81
82 private static final int DEFAULT_NUM_THREADS = 4;
83 @Property(name = NUM_THREAD,
84 intValue = DEFAULT_NUM_THREADS,
85 label = "Number of worker threads")
86 private int numThreads = DEFAULT_NUM_THREADS;
87
alshabib1d2bc402015-07-31 17:04:11 -070088 private final Logger log = getLogger(getClass());
89 private final MeterStoreDelegate delegate = new InternalMeterStoreDelegate();
90
91 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Pier Luigif094c612017-10-14 12:15:02 +020092 private MeterStore store;
alshabib1d2bc402015-07-31 17:04:11 -070093
Andrea Campanellae3708782017-10-16 16:00:21 +020094 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected DriverService driverService;
96
97 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
98 protected DeviceService deviceService;
99
100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
101 protected ComponentConfigService cfgService;
102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
104 protected MastershipService mastershipService;
105
106 private static final int DEFAULT_POLL_FREQUENCY = 30;
107 @Property(name = "fallbackMeterPollFrequency", intValue = DEFAULT_POLL_FREQUENCY,
108 label = "Frequency (in seconds) for polling meters via fallback provider")
109 private int fallbackMeterPollFrequency = DEFAULT_POLL_FREQUENCY;
110
alshabibe1248b62015-08-20 17:21:55 -0700111 private TriConsumer<MeterRequest, MeterStoreResult, Throwable> onComplete;
alshabibeadfc8e2015-08-18 15:40:46 -0700112
Jordi Ortiz31d4d382017-07-19 10:52:26 +0200113 private ExecutorService executorService;
114
Andrea Campanellae3708782017-10-16 16:00:21 +0200115 private final MeterDriverProvider defaultProvider = new MeterDriverProvider();
116
alshabib1d2bc402015-07-31 17:04:11 -0700117 @Activate
Andrea Campanellae3708782017-10-16 16:00:21 +0200118 public void activate(ComponentContext context) {
alshabib58fe6dc2015-08-19 17:16:13 -0700119 store.setDelegate(delegate);
Andrea Campanellae3708782017-10-16 16:00:21 +0200120 cfgService.registerProperties(getClass());
Jordi Ortiz3a6f3072016-11-07 10:23:48 +0100121 eventDispatcher.addSink(MeterEvent.class, listenerRegistry);
alshabib58fe6dc2015-08-19 17:16:13 -0700122
Jordi Ortiz3a6f3072016-11-07 10:23:48 +0100123 onComplete = (request, result, error) -> {
alshabibe1248b62015-08-20 17:21:55 -0700124 request.context().ifPresent(c -> {
alshabibeadfc8e2015-08-18 15:40:46 -0700125 if (error != null) {
alshabibe1248b62015-08-20 17:21:55 -0700126 c.onError(request, MeterFailReason.UNKNOWN);
alshabibeadfc8e2015-08-18 15:40:46 -0700127 } else {
128 if (result.reason().isPresent()) {
alshabibe1248b62015-08-20 17:21:55 -0700129 c.onError(request, result.reason().get());
alshabibeadfc8e2015-08-18 15:40:46 -0700130 } else {
alshabibe1248b62015-08-20 17:21:55 -0700131 c.onSuccess(request);
alshabibeadfc8e2015-08-18 15:40:46 -0700132 }
133 }
134 });
135
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900136 };
Jordi Ortiz31d4d382017-07-19 10:52:26 +0200137
138 executorService = newFixedThreadPool(numThreads,
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900139 groupedThreads(GROUP_THREAD_NAME, WORKER_PATTERN, log));
Andrea Campanellae3708782017-10-16 16:00:21 +0200140 modified(context);
alshabib1d2bc402015-07-31 17:04:11 -0700141 log.info("Started");
142 }
143
Andrea Campanellae3708782017-10-16 16:00:21 +0200144 @Modified
145 public void modified(ComponentContext context) {
146 if (context != null) {
147 readComponentConfiguration(context);
148 }
149 defaultProvider.init(deviceService, createProviderService(defaultProvider),
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900150 mastershipService, fallbackMeterPollFrequency);
Andrea Campanellae3708782017-10-16 16:00:21 +0200151 }
152
alshabib1d2bc402015-07-31 17:04:11 -0700153 @Deactivate
154 public void deactivate() {
Andrea Campanellae3708782017-10-16 16:00:21 +0200155 defaultProvider.terminate();
alshabib58fe6dc2015-08-19 17:16:13 -0700156 store.unsetDelegate(delegate);
Thomas Vachuska11b99fc2017-04-27 12:51:04 -0700157 eventDispatcher.removeSink(MeterEvent.class);
Andrea Campanellae3708782017-10-16 16:00:21 +0200158 cfgService.unregisterProperties(getClass(), false);
Jordi Ortiz31d4d382017-07-19 10:52:26 +0200159 executorService.shutdown();
alshabib1d2bc402015-07-31 17:04:11 -0700160 log.info("Stopped");
161 }
162
Andrea Campanellae3708782017-10-16 16:00:21 +0200163 /**
164 * Extracts properties from the component configuration context.
165 *
166 * @param context the component context
167 */
168 private void readComponentConfiguration(ComponentContext context) {
169 Dictionary<?, ?> properties = context.getProperties();
170
171 String s = get(properties, "fallbackMeterPollFrequency");
172 try {
173 fallbackMeterPollFrequency = isNullOrEmpty(s) ? DEFAULT_POLL_FREQUENCY : Integer.parseInt(s);
174 } catch (NumberFormatException e) {
175 fallbackMeterPollFrequency = DEFAULT_POLL_FREQUENCY;
176 }
177 }
178
179 @Override
180 protected MeterProvider defaultProvider() {
181 return defaultProvider;
182 }
183
alshabib1d2bc402015-07-31 17:04:11 -0700184 @Override
185 protected MeterProviderService createProviderService(MeterProvider provider) {
186 return new InternalMeterProviderService(provider);
187 }
188
189 @Override
alshabibe1248b62015-08-20 17:21:55 -0700190 public Meter submit(MeterRequest request) {
柯志勇10068695d709dba2018-10-23 18:35:49 +0800191 checkNotNull(request, "request cannot be null.");
alshabib70aaa1b2015-09-25 14:30:59 -0700192 MeterId id = allocateMeterId(request.deviceId());
193
alshabibe1248b62015-08-20 17:21:55 -0700194 Meter.Builder mBuilder = DefaultMeter.builder()
195 .forDevice(request.deviceId())
196 .fromApp(request.appId())
197 .withBands(request.bands())
alshabib70aaa1b2015-09-25 14:30:59 -0700198 .withId(id)
alshabibe1248b62015-08-20 17:21:55 -0700199 .withUnit(request.unit());
200
201 if (request.isBurst()) {
202 mBuilder.burst();
203 }
204 DefaultMeter m = (DefaultMeter) mBuilder.build();
alshabib7bb05012015-08-05 10:15:09 -0700205 m.setState(MeterState.PENDING_ADD);
alshabibeadfc8e2015-08-18 15:40:46 -0700206 store.storeMeter(m).whenComplete((result, error) ->
alshabibe1248b62015-08-20 17:21:55 -0700207 onComplete.accept(request, result, error));
208 return m;
alshabib1d2bc402015-07-31 17:04:11 -0700209 }
210
211 @Override
alshabibe1248b62015-08-20 17:21:55 -0700212 public void withdraw(MeterRequest request, MeterId meterId) {
柯志勇10068695d709dba2018-10-23 18:35:49 +0800213 checkNotNull(request, "request cannot be null.");
alshabibe1248b62015-08-20 17:21:55 -0700214 Meter.Builder mBuilder = DefaultMeter.builder()
215 .forDevice(request.deviceId())
216 .fromApp(request.appId())
217 .withBands(request.bands())
218 .withId(meterId)
219 .withUnit(request.unit());
alshabib1d2bc402015-07-31 17:04:11 -0700220
alshabibe1248b62015-08-20 17:21:55 -0700221 if (request.isBurst()) {
222 mBuilder.burst();
223 }
224
225 DefaultMeter m = (DefaultMeter) mBuilder.build();
alshabib7bb05012015-08-05 10:15:09 -0700226 m.setState(MeterState.PENDING_REMOVE);
alshabibeadfc8e2015-08-18 15:40:46 -0700227 store.deleteMeter(m).whenComplete((result, error) ->
alshabibe1248b62015-08-20 17:21:55 -0700228 onComplete.accept(request, result, error));
alshabib1d2bc402015-07-31 17:04:11 -0700229 }
230
231 @Override
alshabib70aaa1b2015-09-25 14:30:59 -0700232 public Meter getMeter(DeviceId deviceId, MeterId id) {
233 MeterKey key = MeterKey.key(deviceId, id);
234 return store.getMeter(key);
alshabib1d2bc402015-07-31 17:04:11 -0700235 }
236
237 @Override
Jian Li1932b932016-01-03 00:35:40 -0800238 public Collection<Meter> getMeters(DeviceId deviceId) {
239 return store.getAllMeters().stream().filter(m ->
240 m.deviceId().equals(deviceId)).collect(Collectors.toList());
241 }
242
243 @Override
alshabib58fe6dc2015-08-19 17:16:13 -0700244 public Collection<Meter> getAllMeters() {
245 return store.getAllMeters();
246 }
247
Pier Luigibdcd9672017-10-13 13:54:48 +0200248 @Override
249 public MeterId allocateMeterId(DeviceId deviceId) {
Pier Luigif094c612017-10-14 12:15:02 +0200250 // We delegate direclty to the store
251 return store.allocateMeterId(deviceId);
alshabib1d2bc402015-07-31 17:04:11 -0700252 }
253
Pier Luigibdcd9672017-10-13 13:54:48 +0200254 @Override
255 public void freeMeterId(DeviceId deviceId, MeterId meterId) {
256 // We delegate direclty to the store
257 store.freeMeterId(deviceId, meterId);
258 }
259
alshabib1d2bc402015-07-31 17:04:11 -0700260 private class InternalMeterProviderService
261 extends AbstractProviderService<MeterProvider>
262 implements MeterProviderService {
263
264 /**
265 * Creates a provider service on behalf of the specified provider.
266 *
267 * @param provider provider to which this service is being issued
268 */
269 protected InternalMeterProviderService(MeterProvider provider) {
270 super(provider);
271 }
272
273 @Override
alshabib7bb05012015-08-05 10:15:09 -0700274 public void meterOperationFailed(MeterOperation operation,
275 MeterFailReason reason) {
276 store.failedMeter(operation, reason);
alshabib1d2bc402015-07-31 17:04:11 -0700277 }
278
279 @Override
280 public void pushMeterMetrics(DeviceId deviceId, Collection<Meter> meterEntries) {
Jordi Ortiz9287b632017-06-22 11:01:37 +0200281 Collection<Meter> allMeters = store.getAllMeters(deviceId);
alshabib5eb79392015-08-19 18:09:55 -0700282
Jordi Ortiz58896912017-03-06 14:51:05 +0100283 Map<MeterId, Meter> meterEntriesMap = meterEntries.stream()
284 .collect(Collectors.toMap(Meter::id, Meter -> Meter));
285
Jordi Ortiz205af452017-06-22 14:25:04 +0200286 // Look for meters defined in onos and missing in the device (restore)
Jordi Ortiz9287b632017-06-22 11:01:37 +0200287 allMeters.stream().forEach(m -> {
288 if ((m.state().equals(MeterState.PENDING_ADD) ||
289 m.state().equals(MeterState.ADDED)) &&
290 !meterEntriesMap.containsKey(m.id())) {
Jordi Ortiz58896912017-03-06 14:51:05 +0100291 // The meter is missing in the device. Reinstall!
Jordi Ortiz205af452017-06-22 14:25:04 +0200292 log.debug("Adding meter missing in device {} {}", deviceId, m);
Jordi Ortiz58896912017-03-06 14:51:05 +0100293 provider().performMeterOperation(deviceId,
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900294 new MeterOperation(m, MeterOperation.Type.ADD));
Jordi Ortiz58896912017-03-06 14:51:05 +0100295 }
Jordi Ortiz58896912017-03-06 14:51:05 +0100296 });
297
Jordi Ortiz205af452017-06-22 14:25:04 +0200298 // Look for meters defined in the device and not in onos (remove)
299 meterEntriesMap.entrySet().stream()
300 .filter(md -> !allMeters.stream().anyMatch(m -> m.id().equals(md.getKey())))
301 .forEach(mio -> {
Jordi Ortiz205af452017-06-22 14:25:04 +0200302 Meter meter = mio.getValue();
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900303 // FIXME: Removing a meter is meaningfull for OpenFlow, but not for P4Runtime.
304 // In P4Runtime meter cells cannot be removed. For the
305 // moment, we make the distinction between OpenFlow and
306 // P4Runtime by looking at the MeterCellType (always
307 // INDEX for OpenFlow).
308 if (meter.meterCellId().type() == MeterCellType.INDEX) {
309 // The meter is missing in onos. Uninstall!
310 log.debug("Remove meter in device not in onos {} {}", deviceId, mio.getKey());
311 provider().performMeterOperation(deviceId,
312 new MeterOperation(meter, MeterOperation.Type.REMOVE));
313 }
314 });
Jordi Ortiz205af452017-06-22 14:25:04 +0200315
alshabib5eb79392015-08-19 18:09:55 -0700316 meterEntries.stream()
Jordi Ortiz9287b632017-06-22 11:01:37 +0200317 .filter(m -> allMeters.stream()
318 .anyMatch(sm -> sm.deviceId().equals(deviceId) && sm.id().equals(m.id())))
alshabib5eb79392015-08-19 18:09:55 -0700319 .forEach(m -> store.updateMeterState(m));
320
Jordi Ortiz9287b632017-06-22 11:01:37 +0200321 allMeters.forEach(m -> {
alshabib5eb79392015-08-19 18:09:55 -0700322 if (m.state() == MeterState.PENDING_ADD) {
323 provider().performMeterOperation(m.deviceId(),
324 new MeterOperation(m,
Konstantinos Kanonakisa45755d2016-03-14 11:31:23 -0500325 MeterOperation.Type.MODIFY));
326 } else if (m.state() == MeterState.PENDING_REMOVE) {
alshabib5eb79392015-08-19 18:09:55 -0700327 store.deleteMeterNow(m);
328 }
329 });
alshabib1d2bc402015-07-31 17:04:11 -0700330 }
Jordi Ortizaa8de492016-12-01 00:21:36 +0100331
332 @Override
333 public void pushMeterFeatures(DeviceId deviceId, MeterFeatures meterfeatures) {
334 store.storeMeterFeatures(meterfeatures);
335 }
336
337 @Override
338 public void deleteMeterFeatures(DeviceId deviceId) {
339 store.deleteMeterFeatures(deviceId);
340 }
alshabib1d2bc402015-07-31 17:04:11 -0700341 }
342
343 private class InternalMeterStoreDelegate implements MeterStoreDelegate {
344
345 @Override
346 public void notify(MeterEvent event) {
alshabibeadfc8e2015-08-18 15:40:46 -0700347 DeviceId deviceId = event.subject().deviceId();
alshabib7bb05012015-08-05 10:15:09 -0700348 switch (event.type()) {
alshabibeadfc8e2015-08-18 15:40:46 -0700349 case METER_ADD_REQ:
Jordi Ortiz31d4d382017-07-19 10:52:26 +0200350 executorService.execute(new MeterInstaller(deviceId, event.subject(),
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900351 MeterOperation.Type.ADD));
alshabib7bb05012015-08-05 10:15:09 -0700352 break;
alshabibeadfc8e2015-08-18 15:40:46 -0700353 case METER_REM_REQ:
Jordi Ortiz31d4d382017-07-19 10:52:26 +0200354 executorService.execute(new MeterInstaller(deviceId, event.subject(),
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900355 MeterOperation.Type.REMOVE));
alshabib7bb05012015-08-05 10:15:09 -0700356 break;
Jordi Ortizdf28ecd2017-03-25 19:22:36 +0100357 case METER_ADDED:
358 log.info("Meter added {}", event.subject());
Jordi Ortize165e1d2017-07-14 14:46:33 +0200359 post(new MeterEvent(MeterEvent.Type.METER_ADDED, event.subject()));
Jordi Ortizdf28ecd2017-03-25 19:22:36 +0100360 break;
361 case METER_REMOVED:
362 log.info("Meter removed {}", event.subject());
Jordi Ortize165e1d2017-07-14 14:46:33 +0200363 post(new MeterEvent(MeterEvent.Type.METER_REMOVED, event.subject()));
Jordi Ortizdf28ecd2017-03-25 19:22:36 +0100364 break;
alshabib7bb05012015-08-05 10:15:09 -0700365 default:
366 log.warn("Unknown meter event {}", event.type());
367 }
alshabib1d2bc402015-07-31 17:04:11 -0700368
369 }
370 }
Jordi Ortiz31d4d382017-07-19 10:52:26 +0200371 /**
372 * Task that passes the meter down to the provider.
373 */
374 private class MeterInstaller implements Runnable {
375 private final DeviceId deviceId;
376 private final Meter meter;
377 private final MeterOperation.Type op;
378
379 public MeterInstaller(DeviceId deviceId, Meter meter, MeterOperation.Type op) {
380 this.deviceId = checkNotNull(deviceId);
381 this.meter = checkNotNull(meter);
382 this.op = checkNotNull(op);
383 }
384
385 @Override
386 public void run() {
387 MeterProvider p = getProvider(this.deviceId);
388 if (p == null) {
389 log.error("Unable to recover {}'s provider", deviceId);
390 return;
391 }
392 p.performMeterOperation(deviceId, new MeterOperation(meter, op));
393 }
394 }
alshabib1d2bc402015-07-31 17:04:11 -0700395
396}