blob: 5f1a5439ab4f37241b842166811281caa5a2b1fc [file] [log] [blame]
alshabibe27055b2015-07-09 21:43:10 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
alshabibe27055b2015-07-09 21:43:10 -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 */
16
17package org.onosproject.provider.of.meter.impl;
18
alshabib1d2bc402015-07-31 17:04:11 -070019
alshabibc7911792015-07-30 17:55:30 -070020import com.google.common.cache.Cache;
21import com.google.common.cache.CacheBuilder;
22import com.google.common.cache.RemovalCause;
23import com.google.common.cache.RemovalNotification;
Yuta HIGUCHI67f2cca2017-01-19 19:31:58 -080024import com.google.common.collect.ImmutableSet;
alshabibc7911792015-07-30 17:55:30 -070025import com.google.common.collect.Maps;
26import org.apache.felix.scr.annotations.Activate;
alshabibe27055b2015-07-09 21:43:10 -070027import org.apache.felix.scr.annotations.Component;
alshabibc7911792015-07-30 17:55:30 -070028import org.apache.felix.scr.annotations.Deactivate;
alshabibe27055b2015-07-09 21:43:10 -070029import org.apache.felix.scr.annotations.Reference;
30import org.apache.felix.scr.annotations.ReferenceCardinality;
alshabib7bb05012015-08-05 10:15:09 -070031import org.onosproject.core.CoreService;
Charles Chana59f9b762017-07-30 18:09:44 -070032import org.onosproject.net.driver.Driver;
33import org.onosproject.net.driver.DriverService;
alshabib10c810b2015-08-18 16:59:04 -070034import org.onosproject.net.meter.Band;
35import org.onosproject.net.meter.DefaultBand;
36import org.onosproject.net.meter.DefaultMeter;
37import org.onosproject.net.meter.Meter;
38import org.onosproject.net.meter.MeterFailReason;
Jordi Ortizaa8de492016-12-01 00:21:36 +010039import org.onosproject.net.meter.MeterFeatures;
alshabib58fe6dc2015-08-19 17:16:13 -070040import org.onosproject.net.meter.MeterId;
alshabib10c810b2015-08-18 16:59:04 -070041import org.onosproject.net.meter.MeterOperation;
42import org.onosproject.net.meter.MeterOperations;
43import org.onosproject.net.meter.MeterProvider;
44import org.onosproject.net.meter.MeterProviderRegistry;
45import org.onosproject.net.meter.MeterProviderService;
46import org.onosproject.net.meter.MeterState;
Yuta HIGUCHI67f2cca2017-01-19 19:31:58 -080047import org.onosproject.net.Device;
alshabib7bb05012015-08-05 10:15:09 -070048import org.onosproject.net.DeviceId;
alshabibe27055b2015-07-09 21:43:10 -070049import org.onosproject.net.provider.AbstractProvider;
50import org.onosproject.net.provider.ProviderId;
alshabibc7911792015-07-30 17:55:30 -070051import org.onosproject.openflow.controller.Dpid;
alshabibe27055b2015-07-09 21:43:10 -070052import org.onosproject.openflow.controller.OpenFlowController;
alshabibc7911792015-07-30 17:55:30 -070053import org.onosproject.openflow.controller.OpenFlowEventListener;
54import org.onosproject.openflow.controller.OpenFlowSwitch;
55import org.onosproject.openflow.controller.OpenFlowSwitchListener;
56import org.onosproject.openflow.controller.RoleState;
Jordi Ortizaa8de492016-12-01 00:21:36 +010057import org.onosproject.provider.of.meter.util.MeterFeaturesBuilder;
alshabibc7911792015-07-30 17:55:30 -070058import org.projectfloodlight.openflow.protocol.OFErrorMsg;
59import org.projectfloodlight.openflow.protocol.OFErrorType;
60import org.projectfloodlight.openflow.protocol.OFMessage;
alshabib7bb05012015-08-05 10:15:09 -070061import org.projectfloodlight.openflow.protocol.OFMeterBandStats;
62import org.projectfloodlight.openflow.protocol.OFMeterConfigStatsReply;
Jordi Ortizaa8de492016-12-01 00:21:36 +010063import org.projectfloodlight.openflow.protocol.OFMeterFeatures;
alshabib7bb05012015-08-05 10:15:09 -070064import org.projectfloodlight.openflow.protocol.OFMeterStats;
65import org.projectfloodlight.openflow.protocol.OFMeterStatsReply;
alshabibc7911792015-07-30 17:55:30 -070066import org.projectfloodlight.openflow.protocol.OFPortStatus;
67import org.projectfloodlight.openflow.protocol.OFStatsReply;
alshabib7bb05012015-08-05 10:15:09 -070068import org.projectfloodlight.openflow.protocol.OFStatsType;
alshabibc7911792015-07-30 17:55:30 -070069import org.projectfloodlight.openflow.protocol.OFVersion;
70import org.projectfloodlight.openflow.protocol.errormsg.OFMeterModFailedErrorMsg;
alshabibe27055b2015-07-09 21:43:10 -070071import org.slf4j.Logger;
72
alshabib7bb05012015-08-05 10:15:09 -070073import java.util.Collection;
Yuta HIGUCHI67f2cca2017-01-19 19:31:58 -080074import java.util.EnumSet;
alshabib7bb05012015-08-05 10:15:09 -070075import java.util.List;
alshabibc7911792015-07-30 17:55:30 -070076import java.util.Map;
Yuta HIGUCHI67f2cca2017-01-19 19:31:58 -080077import java.util.Set;
alshabibc7911792015-07-30 17:55:30 -070078import java.util.concurrent.TimeUnit;
79import java.util.concurrent.atomic.AtomicLong;
alshabib7bb05012015-08-05 10:15:09 -070080import java.util.stream.Collectors;
alshabibc7911792015-07-30 17:55:30 -070081
Jordi Ortizaa8de492016-12-01 00:21:36 +010082import static org.onosproject.net.DeviceId.deviceId;
83import static org.onosproject.openflow.controller.Dpid.uri;
alshabibe27055b2015-07-09 21:43:10 -070084import static org.slf4j.LoggerFactory.getLogger;
85
86/**
87 * Provider which uses an OpenFlow controller to handle meters.
88 */
alshabib58fe6dc2015-08-19 17:16:13 -070089@Component(immediate = true, enabled = true)
alshabibe27055b2015-07-09 21:43:10 -070090public class OpenFlowMeterProvider extends AbstractProvider implements MeterProvider {
91
92 private final Logger log = getLogger(getClass());
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected OpenFlowController controller;
96
alshabibc7911792015-07-30 17:55:30 -070097 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
98 protected MeterProviderRegistry providerRegistry;
99
alshabib7bb05012015-08-05 10:15:09 -0700100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
101 protected CoreService coreService;
102
Charles Chana59f9b762017-07-30 18:09:44 -0700103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
104 protected DriverService driverService;
105
alshabibc7911792015-07-30 17:55:30 -0700106 private MeterProviderService providerService;
107
108 private static final AtomicLong XID_COUNTER = new AtomicLong(1);
109
110 static final int POLL_INTERVAL = 10;
111 static final long TIMEOUT = 30;
112
alshabib7bb05012015-08-05 10:15:09 -0700113 private Cache<Long, MeterOperation> pendingOperations;
alshabibc7911792015-07-30 17:55:30 -0700114
115
116 private InternalMeterListener listener = new InternalMeterListener();
117 private Map<Dpid, MeterStatsCollector> collectors = Maps.newHashMap();
alshabibe27055b2015-07-09 21:43:10 -0700118
Yuta HIGUCHI67f2cca2017-01-19 19:31:58 -0800119 private static final Set<Device.Type> NO_METER_SUPPORT =
120 ImmutableSet.copyOf(EnumSet.of(Device.Type.ROADM,
121 Device.Type.ROADM_OTN,
122 Device.Type.FIBER_SWITCH,
123 Device.Type.OTN));
124
alshabibe27055b2015-07-09 21:43:10 -0700125 /**
126 * Creates a OpenFlow meter provider.
127 */
128 public OpenFlowMeterProvider() {
129 super(new ProviderId("of", "org.onosproject.provider.meter"));
130 }
alshabibbc371962015-07-09 22:26:21 -0700131
alshabibc7911792015-07-30 17:55:30 -0700132 @Activate
133 public void activate() {
134 providerService = providerRegistry.register(this);
135
136 pendingOperations = CacheBuilder.newBuilder()
137 .expireAfterWrite(TIMEOUT, TimeUnit.SECONDS)
alshabib7bb05012015-08-05 10:15:09 -0700138 .removalListener((RemovalNotification<Long, MeterOperation> notification) -> {
alshabibc7911792015-07-30 17:55:30 -0700139 if (notification.getCause() == RemovalCause.EXPIRED) {
140 providerService.meterOperationFailed(notification.getValue(),
141 MeterFailReason.TIMEOUT);
142 }
143 }).build();
144
145 controller.addEventListener(listener);
146 controller.addListener(listener);
147
148 controller.getSwitches().forEach((sw -> createStatsCollection(sw)));
149 }
150
151 @Deactivate
152 public void deactivate() {
153 providerRegistry.unregister(this);
Charles Chanecfdfb72015-11-24 19:05:50 -0800154 collectors.values().forEach(MeterStatsCollector::stop);
155 collectors.clear();
alshabibc7911792015-07-30 17:55:30 -0700156 controller.removeEventListener(listener);
157 controller.removeListener(listener);
158 providerService = null;
159 }
160
alshabibbc371962015-07-09 22:26:21 -0700161 @Override
162 public void performMeterOperation(DeviceId deviceId, MeterOperations meterOps) {
alshabibc7911792015-07-30 17:55:30 -0700163 Dpid dpid = Dpid.dpid(deviceId.uri());
164 OpenFlowSwitch sw = controller.getSwitch(dpid);
165 if (sw == null) {
166 log.error("Unknown device {}", deviceId);
167 meterOps.operations().forEach(op ->
168 providerService.meterOperationFailed(op,
169 MeterFailReason.UNKNOWN_DEVICE)
170 );
171 return;
172 }
alshabibbc371962015-07-09 22:26:21 -0700173
alshabibc7911792015-07-30 17:55:30 -0700174 meterOps.operations().forEach(op -> performOperation(sw, op));
alshabibbc371962015-07-09 22:26:21 -0700175 }
176
177 @Override
178 public void performMeterOperation(DeviceId deviceId, MeterOperation meterOp) {
alshabibc7911792015-07-30 17:55:30 -0700179 Dpid dpid = Dpid.dpid(deviceId.uri());
180 OpenFlowSwitch sw = controller.getSwitch(dpid);
181 if (sw == null) {
182 log.error("Unknown device {}", deviceId);
183 providerService.meterOperationFailed(meterOp,
184 MeterFailReason.UNKNOWN_DEVICE);
185 return;
186 }
alshabibbc371962015-07-09 22:26:21 -0700187
alshabib7bb05012015-08-05 10:15:09 -0700188 performOperation(sw, meterOp);
189
Jordi Ortiz9fe79a22017-04-25 12:57:11 +0200190 if (meterOp.type().equals(MeterOperation.Type.REMOVE)) {
191 forceMeterStats(deviceId);
192 }
193
194 }
195
196 private void forceMeterStats(DeviceId deviceId) {
197 Dpid dpid = Dpid.dpid(deviceId.uri());
198 OpenFlowSwitch sw = controller.getSwitch(dpid);
199
200 MeterStatsCollector once = new MeterStatsCollector(sw, 1);
Palash Kalaf95c38b2017-05-15 23:52:48 +0900201 once.sendMeterStatisticRequest();
Jordi Ortiz9fe79a22017-04-25 12:57:11 +0200202
alshabibbc371962015-07-09 22:26:21 -0700203 }
alshabibc7911792015-07-30 17:55:30 -0700204
205 private void performOperation(OpenFlowSwitch sw, MeterOperation op) {
206
207 pendingOperations.put(op.meter().id().id(), op);
208
209
210 Meter meter = op.meter();
211 MeterModBuilder builder = MeterModBuilder.builder(meter.id().id(), sw.factory());
212 if (meter.isBurst()) {
213 builder.burst();
214 }
215 builder.withBands(meter.bands())
216 .withId(meter.id())
217 .withRateUnit(meter.unit());
218
219 switch (op.type()) {
220 case ADD:
221 sw.sendMsg(builder.add());
222 break;
223 case REMOVE:
224 sw.sendMsg(builder.remove());
225 break;
226 case MODIFY:
227 sw.sendMsg(builder.modify());
228 break;
229 default:
230 log.warn("Unknown Meter command {}; not sending anything",
231 op.type());
232 providerService.meterOperationFailed(op,
233 MeterFailReason.UNKNOWN_COMMAND);
234 }
235
236 }
237
238 private void createStatsCollection(OpenFlowSwitch sw) {
Madan Jampani84382b92016-06-22 08:26:49 -0700239 if (sw != null && isMeterSupported(sw)) {
alshabibc7911792015-07-30 17:55:30 -0700240 MeterStatsCollector msc = new MeterStatsCollector(sw, POLL_INTERVAL);
Thomas Vachuskaa394b952016-06-14 15:02:09 -0700241 stopCollectorIfNeeded(collectors.put(new Dpid(sw.getId()), msc));
Palash Kalaa439afe2017-05-16 14:53:15 +0900242 msc.start();
Thomas Vachuskaa394b952016-06-14 15:02:09 -0700243 }
244 }
245
246 private void stopCollectorIfNeeded(MeterStatsCollector collector) {
247 if (collector != null) {
248 collector.stop();
alshabibc7911792015-07-30 17:55:30 -0700249 }
250 }
251
Charles Chan14967c22015-12-07 11:11:50 -0800252 // TODO: ONOS-3546 Support per device enabling/disabling via network config
alshabibc7911792015-07-30 17:55:30 -0700253 private boolean isMeterSupported(OpenFlowSwitch sw) {
254 if (sw.factory().getVersion() == OFVersion.OF_10 ||
255 sw.factory().getVersion() == OFVersion.OF_11 ||
Charles Chan14967c22015-12-07 11:11:50 -0800256 sw.factory().getVersion() == OFVersion.OF_12 ||
Yuta HIGUCHI67f2cca2017-01-19 19:31:58 -0800257 NO_METER_SUPPORT.contains(sw.deviceType()) ||
Charles Chana59f9b762017-07-30 18:09:44 -0700258 !isMeterCapable(sw)) {
alshabibc7911792015-07-30 17:55:30 -0700259 return false;
260 }
261
262 return true;
263 }
264
Charles Chana59f9b762017-07-30 18:09:44 -0700265 /**
266 * Determine whether the given switch is meter-capable.
267 *
268 * @param sw switch
269 * @return the boolean value of meterCapable property, or true if it is not configured.
270 */
271 private boolean isMeterCapable(OpenFlowSwitch sw) {
272 Driver driver = driverService.getDriver(DeviceId.deviceId(Dpid.uri(sw.getDpid())));
273 String isMeterCapable = driver.getProperty(METER_CAPABLE);
274 return isMeterCapable == null || Boolean.parseBoolean(isMeterCapable);
275 }
276
alshabibc7911792015-07-30 17:55:30 -0700277 private void pushMeterStats(Dpid dpid, OFStatsReply msg) {
alshabib7bb05012015-08-05 10:15:09 -0700278 DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid));
alshabibc7911792015-07-30 17:55:30 -0700279
alshabib7bb05012015-08-05 10:15:09 -0700280 if (msg.getStatsType() == OFStatsType.METER) {
281 OFMeterStatsReply reply = (OFMeterStatsReply) msg;
282 Collection<Meter> meters = buildMeters(deviceId, reply.getEntries());
283 //TODO do meter accounting here.
284 providerService.pushMeterMetrics(deviceId, meters);
285 } else if (msg.getStatsType() == OFStatsType.METER_CONFIG) {
286 OFMeterConfigStatsReply reply = (OFMeterConfigStatsReply) msg;
287 // FIXME: Map<Long, Meter> meters = collectMeters(deviceId, reply);
288 }
289
290 }
291
Jordi Ortizaa8de492016-12-01 00:21:36 +0100292 private MeterFeatures buildMeterFeatures(Dpid dpid, OFMeterFeatures mf) {
293 if (mf != null) {
294 return new MeterFeaturesBuilder(mf, deviceId(uri(dpid)))
295 .build();
296 } else {
297 // This will usually happen for OpenFlow devices prior to 1.3
298 return MeterFeaturesBuilder.noMeterFeatures(deviceId(uri(dpid)));
299 }
300 }
301
302 private void pushMeterFeatures(Dpid dpid, OFMeterFeatures meterFeatures) {
303 providerService.pushMeterFeatures(deviceId(uri(dpid)),
304 buildMeterFeatures(dpid, meterFeatures));
305 }
306
307 private void destroyMeterFeatures(Dpid dpid) {
308 providerService.deleteMeterFeatures(deviceId(uri(dpid)));
309 }
310
alshabib7bb05012015-08-05 10:15:09 -0700311 private Map<Long, Meter> collectMeters(DeviceId deviceId,
312 OFMeterConfigStatsReply reply) {
313 return Maps.newHashMap();
314 //TODO: Needs a fix to be applied to loxi MeterConfig stat is incorrect
315 }
316
317 private Collection<Meter> buildMeters(DeviceId deviceId,
318 List<OFMeterStats> entries) {
319 return entries.stream().map(stat -> {
320 DefaultMeter.Builder builder = DefaultMeter.builder();
321 Collection<Band> bands = buildBands(stat.getBandStats());
322 builder.forDevice(deviceId)
alshabib58fe6dc2015-08-19 17:16:13 -0700323 .withId(MeterId.meterId(stat.getMeterId()))
alshabib7bb05012015-08-05 10:15:09 -0700324 //FIXME: need to encode appId in meter id, but that makes
325 // things a little annoying for debugging
326 .fromApp(coreService.getAppId("org.onosproject.core"))
327 .withBands(bands);
328 DefaultMeter meter = builder.build();
329 meter.setState(MeterState.ADDED);
330 meter.setLife(stat.getDurationSec());
331 meter.setProcessedBytes(stat.getByteInCount().getValue());
332 meter.setProcessedPackets(stat.getPacketInCount().getValue());
333 meter.setReferenceCount(stat.getFlowCount());
334
335 // marks the meter as seen on the dataplane
336 pendingOperations.invalidate(stat.getMeterId());
337 return meter;
338 }).collect(Collectors.toSet());
339 }
340
341 private Collection<Band> buildBands(List<OFMeterBandStats> bandStats) {
342 return bandStats.stream().map(stat -> {
Jordi Ortizaf75c132017-06-22 16:06:36 +0200343 DefaultBand band = ((DefaultBand.Builder) DefaultBand.builder().ofType(DefaultBand.Type.DROP)).build();
alshabib7bb05012015-08-05 10:15:09 -0700344 band.setBytes(stat.getByteBandCount().getValue());
345 band.setPackets(stat.getPacketBandCount().getValue());
346 return band;
Jordi Ortizaf75c132017-06-22 16:06:36 +0200347 }).collect(Collectors.toList());
alshabibc7911792015-07-30 17:55:30 -0700348 }
349
350 private void signalMeterError(OFMeterModFailedErrorMsg meterError,
351 MeterOperation op) {
352 switch (meterError.getCode()) {
353 case UNKNOWN:
354 providerService.meterOperationFailed(op,
355 MeterFailReason.UNKNOWN_DEVICE);
356 break;
357 case METER_EXISTS:
358 providerService.meterOperationFailed(op,
359 MeterFailReason.EXISTING_METER);
360 break;
361 case INVALID_METER:
362 providerService.meterOperationFailed(op,
363 MeterFailReason.INVALID_METER);
364 break;
365 case UNKNOWN_METER:
366 providerService.meterOperationFailed(op,
367 MeterFailReason.UNKNOWN);
368 break;
369 case BAD_COMMAND:
370 providerService.meterOperationFailed(op,
371 MeterFailReason.UNKNOWN_COMMAND);
372 break;
373 case BAD_FLAGS:
374 providerService.meterOperationFailed(op,
375 MeterFailReason.UNKNOWN_FLAGS);
376 break;
377 case BAD_RATE:
378 providerService.meterOperationFailed(op,
379 MeterFailReason.BAD_RATE);
380 break;
381 case BAD_BURST:
382 providerService.meterOperationFailed(op,
383 MeterFailReason.BAD_BURST);
384 break;
385 case BAD_BAND:
386 providerService.meterOperationFailed(op,
387 MeterFailReason.BAD_BAND);
388 break;
389 case BAD_BAND_VALUE:
390 providerService.meterOperationFailed(op,
391 MeterFailReason.BAD_BAND_VALUE);
392 break;
393 case OUT_OF_METERS:
394 providerService.meterOperationFailed(op,
395 MeterFailReason.OUT_OF_METERS);
396 break;
397 case OUT_OF_BANDS:
398 providerService.meterOperationFailed(op,
399 MeterFailReason.OUT_OF_BANDS);
400 break;
401 default:
402 providerService.meterOperationFailed(op,
403 MeterFailReason.UNKNOWN);
404 }
405 }
406
407 private class InternalMeterListener
408 implements OpenFlowSwitchListener, OpenFlowEventListener {
409 @Override
410 public void handleMessage(Dpid dpid, OFMessage msg) {
411 switch (msg.getType()) {
412 case STATS_REPLY:
413 pushMeterStats(dpid, (OFStatsReply) msg);
414 break;
415 case ERROR:
416 OFErrorMsg error = (OFErrorMsg) msg;
417 if (error.getErrType() == OFErrorType.METER_MOD_FAILED) {
418 MeterOperation op =
419 pendingOperations.getIfPresent(error.getXid());
420 pendingOperations.invalidate(error.getXid());
421 if (op == null) {
422 log.warn("Unknown Meter operation failed {}", error);
423 } else {
424 OFMeterModFailedErrorMsg meterError =
425 (OFMeterModFailedErrorMsg) error;
426 signalMeterError(meterError, op);
427 }
428 }
429 break;
430 default:
431 break;
432 }
433
434 }
435
436 @Override
437 public void switchAdded(Dpid dpid) {
438 createStatsCollection(controller.getSwitch(dpid));
Jordi Ortizaa8de492016-12-01 00:21:36 +0100439 pushMeterFeatures(dpid, controller.getSwitch(dpid).getMeterFeatures());
alshabibc7911792015-07-30 17:55:30 -0700440 }
441
442 @Override
443 public void switchRemoved(Dpid dpid) {
Thomas Vachuskaa394b952016-06-14 15:02:09 -0700444 stopCollectorIfNeeded(collectors.remove(dpid));
Jordi Ortizaa8de492016-12-01 00:21:36 +0100445 destroyMeterFeatures(dpid);
alshabibc7911792015-07-30 17:55:30 -0700446 }
447
448 @Override
449 public void switchChanged(Dpid dpid) {
450
451 }
452
453 @Override
454 public void portChanged(Dpid dpid, OFPortStatus status) {
455
456 }
457
458 @Override
459 public void receivedRoleReply(Dpid dpid, RoleState requested, RoleState response) {
460
461 }
462 }
463
464
465
alshabibe27055b2015-07-09 21:43:10 -0700466}