blob: 04e3e56ef1e8b2dafe8dd63aa1290f25a576b431 [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) {
191
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) {
213 Meter.Builder mBuilder = DefaultMeter.builder()
214 .forDevice(request.deviceId())
215 .fromApp(request.appId())
216 .withBands(request.bands())
217 .withId(meterId)
218 .withUnit(request.unit());
alshabib1d2bc402015-07-31 17:04:11 -0700219
alshabibe1248b62015-08-20 17:21:55 -0700220 if (request.isBurst()) {
221 mBuilder.burst();
222 }
223
224 DefaultMeter m = (DefaultMeter) mBuilder.build();
alshabib7bb05012015-08-05 10:15:09 -0700225 m.setState(MeterState.PENDING_REMOVE);
alshabibeadfc8e2015-08-18 15:40:46 -0700226 store.deleteMeter(m).whenComplete((result, error) ->
alshabibe1248b62015-08-20 17:21:55 -0700227 onComplete.accept(request, result, error));
alshabib1d2bc402015-07-31 17:04:11 -0700228 }
229
230 @Override
alshabib70aaa1b2015-09-25 14:30:59 -0700231 public Meter getMeter(DeviceId deviceId, MeterId id) {
232 MeterKey key = MeterKey.key(deviceId, id);
233 return store.getMeter(key);
alshabib1d2bc402015-07-31 17:04:11 -0700234 }
235
236 @Override
Jian Li1932b932016-01-03 00:35:40 -0800237 public Collection<Meter> getMeters(DeviceId deviceId) {
238 return store.getAllMeters().stream().filter(m ->
239 m.deviceId().equals(deviceId)).collect(Collectors.toList());
240 }
241
242 @Override
alshabib58fe6dc2015-08-19 17:16:13 -0700243 public Collection<Meter> getAllMeters() {
244 return store.getAllMeters();
245 }
246
Pier Luigibdcd9672017-10-13 13:54:48 +0200247 @Override
248 public MeterId allocateMeterId(DeviceId deviceId) {
Pier Luigif094c612017-10-14 12:15:02 +0200249 // We delegate direclty to the store
250 return store.allocateMeterId(deviceId);
alshabib1d2bc402015-07-31 17:04:11 -0700251 }
252
Pier Luigibdcd9672017-10-13 13:54:48 +0200253 @Override
254 public void freeMeterId(DeviceId deviceId, MeterId meterId) {
255 // We delegate direclty to the store
256 store.freeMeterId(deviceId, meterId);
257 }
258
alshabib1d2bc402015-07-31 17:04:11 -0700259 private class InternalMeterProviderService
260 extends AbstractProviderService<MeterProvider>
261 implements MeterProviderService {
262
263 /**
264 * Creates a provider service on behalf of the specified provider.
265 *
266 * @param provider provider to which this service is being issued
267 */
268 protected InternalMeterProviderService(MeterProvider provider) {
269 super(provider);
270 }
271
272 @Override
alshabib7bb05012015-08-05 10:15:09 -0700273 public void meterOperationFailed(MeterOperation operation,
274 MeterFailReason reason) {
275 store.failedMeter(operation, reason);
alshabib1d2bc402015-07-31 17:04:11 -0700276 }
277
278 @Override
279 public void pushMeterMetrics(DeviceId deviceId, Collection<Meter> meterEntries) {
Jordi Ortiz9287b632017-06-22 11:01:37 +0200280 Collection<Meter> allMeters = store.getAllMeters(deviceId);
alshabib5eb79392015-08-19 18:09:55 -0700281
Jordi Ortiz58896912017-03-06 14:51:05 +0100282 Map<MeterId, Meter> meterEntriesMap = meterEntries.stream()
283 .collect(Collectors.toMap(Meter::id, Meter -> Meter));
284
Jordi Ortiz205af452017-06-22 14:25:04 +0200285 // Look for meters defined in onos and missing in the device (restore)
Jordi Ortiz9287b632017-06-22 11:01:37 +0200286 allMeters.stream().forEach(m -> {
287 if ((m.state().equals(MeterState.PENDING_ADD) ||
288 m.state().equals(MeterState.ADDED)) &&
289 !meterEntriesMap.containsKey(m.id())) {
Jordi Ortiz58896912017-03-06 14:51:05 +0100290 // The meter is missing in the device. Reinstall!
Jordi Ortiz205af452017-06-22 14:25:04 +0200291 log.debug("Adding meter missing in device {} {}", deviceId, m);
Jordi Ortiz58896912017-03-06 14:51:05 +0100292 provider().performMeterOperation(deviceId,
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900293 new MeterOperation(m, MeterOperation.Type.ADD));
Jordi Ortiz58896912017-03-06 14:51:05 +0100294 }
Jordi Ortiz58896912017-03-06 14:51:05 +0100295 });
296
Jordi Ortiz205af452017-06-22 14:25:04 +0200297 // Look for meters defined in the device and not in onos (remove)
298 meterEntriesMap.entrySet().stream()
299 .filter(md -> !allMeters.stream().anyMatch(m -> m.id().equals(md.getKey())))
300 .forEach(mio -> {
Jordi Ortiz205af452017-06-22 14:25:04 +0200301 Meter meter = mio.getValue();
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900302 // FIXME: Removing a meter is meaningfull for OpenFlow, but not for P4Runtime.
303 // In P4Runtime meter cells cannot be removed. For the
304 // moment, we make the distinction between OpenFlow and
305 // P4Runtime by looking at the MeterCellType (always
306 // INDEX for OpenFlow).
307 if (meter.meterCellId().type() == MeterCellType.INDEX) {
308 // The meter is missing in onos. Uninstall!
309 log.debug("Remove meter in device not in onos {} {}", deviceId, mio.getKey());
310 provider().performMeterOperation(deviceId,
311 new MeterOperation(meter, MeterOperation.Type.REMOVE));
312 }
313 });
Jordi Ortiz205af452017-06-22 14:25:04 +0200314
alshabib5eb79392015-08-19 18:09:55 -0700315 meterEntries.stream()
Jordi Ortiz9287b632017-06-22 11:01:37 +0200316 .filter(m -> allMeters.stream()
317 .anyMatch(sm -> sm.deviceId().equals(deviceId) && sm.id().equals(m.id())))
alshabib5eb79392015-08-19 18:09:55 -0700318 .forEach(m -> store.updateMeterState(m));
319
Jordi Ortiz9287b632017-06-22 11:01:37 +0200320 allMeters.forEach(m -> {
alshabib5eb79392015-08-19 18:09:55 -0700321 if (m.state() == MeterState.PENDING_ADD) {
322 provider().performMeterOperation(m.deviceId(),
323 new MeterOperation(m,
Konstantinos Kanonakisa45755d2016-03-14 11:31:23 -0500324 MeterOperation.Type.MODIFY));
325 } else if (m.state() == MeterState.PENDING_REMOVE) {
alshabib5eb79392015-08-19 18:09:55 -0700326 store.deleteMeterNow(m);
327 }
328 });
alshabib1d2bc402015-07-31 17:04:11 -0700329 }
Jordi Ortizaa8de492016-12-01 00:21:36 +0100330
331 @Override
332 public void pushMeterFeatures(DeviceId deviceId, MeterFeatures meterfeatures) {
333 store.storeMeterFeatures(meterfeatures);
334 }
335
336 @Override
337 public void deleteMeterFeatures(DeviceId deviceId) {
338 store.deleteMeterFeatures(deviceId);
339 }
alshabib1d2bc402015-07-31 17:04:11 -0700340 }
341
342 private class InternalMeterStoreDelegate implements MeterStoreDelegate {
343
344 @Override
345 public void notify(MeterEvent event) {
alshabibeadfc8e2015-08-18 15:40:46 -0700346 DeviceId deviceId = event.subject().deviceId();
alshabib7bb05012015-08-05 10:15:09 -0700347 switch (event.type()) {
alshabibeadfc8e2015-08-18 15:40:46 -0700348 case METER_ADD_REQ:
Jordi Ortiz31d4d382017-07-19 10:52:26 +0200349 executorService.execute(new MeterInstaller(deviceId, event.subject(),
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900350 MeterOperation.Type.ADD));
alshabib7bb05012015-08-05 10:15:09 -0700351 break;
alshabibeadfc8e2015-08-18 15:40:46 -0700352 case METER_REM_REQ:
Jordi Ortiz31d4d382017-07-19 10:52:26 +0200353 executorService.execute(new MeterInstaller(deviceId, event.subject(),
Frank Wangd7e3b4b2017-09-24 13:37:54 +0900354 MeterOperation.Type.REMOVE));
alshabib7bb05012015-08-05 10:15:09 -0700355 break;
Jordi Ortizdf28ecd2017-03-25 19:22:36 +0100356 case METER_ADDED:
357 log.info("Meter added {}", event.subject());
Jordi Ortize165e1d2017-07-14 14:46:33 +0200358 post(new MeterEvent(MeterEvent.Type.METER_ADDED, event.subject()));
Jordi Ortizdf28ecd2017-03-25 19:22:36 +0100359 break;
360 case METER_REMOVED:
361 log.info("Meter removed {}", event.subject());
Jordi Ortize165e1d2017-07-14 14:46:33 +0200362 post(new MeterEvent(MeterEvent.Type.METER_REMOVED, event.subject()));
Jordi Ortizdf28ecd2017-03-25 19:22:36 +0100363 break;
alshabib7bb05012015-08-05 10:15:09 -0700364 default:
365 log.warn("Unknown meter event {}", event.type());
366 }
alshabib1d2bc402015-07-31 17:04:11 -0700367
368 }
369 }
Jordi Ortiz31d4d382017-07-19 10:52:26 +0200370 /**
371 * Task that passes the meter down to the provider.
372 */
373 private class MeterInstaller implements Runnable {
374 private final DeviceId deviceId;
375 private final Meter meter;
376 private final MeterOperation.Type op;
377
378 public MeterInstaller(DeviceId deviceId, Meter meter, MeterOperation.Type op) {
379 this.deviceId = checkNotNull(deviceId);
380 this.meter = checkNotNull(meter);
381 this.op = checkNotNull(op);
382 }
383
384 @Override
385 public void run() {
386 MeterProvider p = getProvider(this.deviceId);
387 if (p == null) {
388 log.error("Unable to recover {}'s provider", deviceId);
389 return;
390 }
391 p.performMeterOperation(deviceId, new MeterOperation(meter, op));
392 }
393 }
alshabib1d2bc402015-07-31 17:04:11 -0700394
395}