blob: 147e124fba0b90f78bca4e8349891a58c58cccdc [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;
alshabib10c810b2015-08-18 16:59:04 -070032import org.onosproject.net.meter.Band;
33import org.onosproject.net.meter.DefaultBand;
34import org.onosproject.net.meter.DefaultMeter;
35import org.onosproject.net.meter.Meter;
36import org.onosproject.net.meter.MeterFailReason;
Jordi Ortizaa8de492016-12-01 00:21:36 +010037import org.onosproject.net.meter.MeterFeatures;
alshabib58fe6dc2015-08-19 17:16:13 -070038import org.onosproject.net.meter.MeterId;
alshabib10c810b2015-08-18 16:59:04 -070039import org.onosproject.net.meter.MeterOperation;
40import org.onosproject.net.meter.MeterOperations;
41import org.onosproject.net.meter.MeterProvider;
42import org.onosproject.net.meter.MeterProviderRegistry;
43import org.onosproject.net.meter.MeterProviderService;
44import org.onosproject.net.meter.MeterState;
Yuta HIGUCHI67f2cca2017-01-19 19:31:58 -080045import org.onosproject.net.Device;
alshabib7bb05012015-08-05 10:15:09 -070046import org.onosproject.net.DeviceId;
alshabibe27055b2015-07-09 21:43:10 -070047import org.onosproject.net.provider.AbstractProvider;
48import org.onosproject.net.provider.ProviderId;
alshabibc7911792015-07-30 17:55:30 -070049import org.onosproject.openflow.controller.Dpid;
alshabibe27055b2015-07-09 21:43:10 -070050import org.onosproject.openflow.controller.OpenFlowController;
alshabibc7911792015-07-30 17:55:30 -070051import org.onosproject.openflow.controller.OpenFlowEventListener;
52import org.onosproject.openflow.controller.OpenFlowSwitch;
53import org.onosproject.openflow.controller.OpenFlowSwitchListener;
54import org.onosproject.openflow.controller.RoleState;
Jordi Ortizaa8de492016-12-01 00:21:36 +010055import org.onosproject.provider.of.meter.util.MeterFeaturesBuilder;
alshabibc7911792015-07-30 17:55:30 -070056import org.projectfloodlight.openflow.protocol.OFErrorMsg;
57import org.projectfloodlight.openflow.protocol.OFErrorType;
58import org.projectfloodlight.openflow.protocol.OFMessage;
alshabib7bb05012015-08-05 10:15:09 -070059import org.projectfloodlight.openflow.protocol.OFMeterBandStats;
60import org.projectfloodlight.openflow.protocol.OFMeterConfigStatsReply;
Jordi Ortizaa8de492016-12-01 00:21:36 +010061import org.projectfloodlight.openflow.protocol.OFMeterFeatures;
alshabib7bb05012015-08-05 10:15:09 -070062import org.projectfloodlight.openflow.protocol.OFMeterStats;
63import org.projectfloodlight.openflow.protocol.OFMeterStatsReply;
alshabibc7911792015-07-30 17:55:30 -070064import org.projectfloodlight.openflow.protocol.OFPortStatus;
65import org.projectfloodlight.openflow.protocol.OFStatsReply;
alshabib7bb05012015-08-05 10:15:09 -070066import org.projectfloodlight.openflow.protocol.OFStatsType;
alshabibc7911792015-07-30 17:55:30 -070067import org.projectfloodlight.openflow.protocol.OFVersion;
68import org.projectfloodlight.openflow.protocol.errormsg.OFMeterModFailedErrorMsg;
alshabibe27055b2015-07-09 21:43:10 -070069import org.slf4j.Logger;
70
alshabib7bb05012015-08-05 10:15:09 -070071import java.util.Collection;
Yuta HIGUCHI67f2cca2017-01-19 19:31:58 -080072import java.util.EnumSet;
alshabib7bb05012015-08-05 10:15:09 -070073import java.util.List;
alshabibc7911792015-07-30 17:55:30 -070074import java.util.Map;
Yuta HIGUCHI67f2cca2017-01-19 19:31:58 -080075import java.util.Set;
alshabibc7911792015-07-30 17:55:30 -070076import java.util.concurrent.TimeUnit;
77import java.util.concurrent.atomic.AtomicLong;
alshabib7bb05012015-08-05 10:15:09 -070078import java.util.stream.Collectors;
alshabibc7911792015-07-30 17:55:30 -070079
Jordi Ortizaa8de492016-12-01 00:21:36 +010080import static org.onosproject.net.DeviceId.deviceId;
81import static org.onosproject.openflow.controller.Dpid.uri;
alshabibe27055b2015-07-09 21:43:10 -070082import static org.slf4j.LoggerFactory.getLogger;
83
84/**
85 * Provider which uses an OpenFlow controller to handle meters.
86 */
alshabib58fe6dc2015-08-19 17:16:13 -070087@Component(immediate = true, enabled = true)
alshabibe27055b2015-07-09 21:43:10 -070088public class OpenFlowMeterProvider extends AbstractProvider implements MeterProvider {
89
alshabibc7911792015-07-30 17:55:30 -070090
alshabibe27055b2015-07-09 21:43:10 -070091 private final Logger log = getLogger(getClass());
92
93 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
94 protected OpenFlowController controller;
95
alshabibc7911792015-07-30 17:55:30 -070096 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
97 protected MeterProviderRegistry providerRegistry;
98
alshabib7bb05012015-08-05 10:15:09 -070099 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected CoreService coreService;
101
alshabibc7911792015-07-30 17:55:30 -0700102 private MeterProviderService providerService;
103
104 private static final AtomicLong XID_COUNTER = new AtomicLong(1);
105
106 static final int POLL_INTERVAL = 10;
107 static final long TIMEOUT = 30;
108
alshabib7bb05012015-08-05 10:15:09 -0700109 private Cache<Long, MeterOperation> pendingOperations;
alshabibc7911792015-07-30 17:55:30 -0700110
111
112 private InternalMeterListener listener = new InternalMeterListener();
113 private Map<Dpid, MeterStatsCollector> collectors = Maps.newHashMap();
alshabibe27055b2015-07-09 21:43:10 -0700114
Yuta HIGUCHI67f2cca2017-01-19 19:31:58 -0800115 private static final Set<Device.Type> NO_METER_SUPPORT =
116 ImmutableSet.copyOf(EnumSet.of(Device.Type.ROADM,
117 Device.Type.ROADM_OTN,
118 Device.Type.FIBER_SWITCH,
119 Device.Type.OTN));
120
alshabibe27055b2015-07-09 21:43:10 -0700121 /**
122 * Creates a OpenFlow meter provider.
123 */
124 public OpenFlowMeterProvider() {
125 super(new ProviderId("of", "org.onosproject.provider.meter"));
126 }
alshabibbc371962015-07-09 22:26:21 -0700127
alshabibc7911792015-07-30 17:55:30 -0700128 @Activate
129 public void activate() {
130 providerService = providerRegistry.register(this);
131
132 pendingOperations = CacheBuilder.newBuilder()
133 .expireAfterWrite(TIMEOUT, TimeUnit.SECONDS)
alshabib7bb05012015-08-05 10:15:09 -0700134 .removalListener((RemovalNotification<Long, MeterOperation> notification) -> {
alshabibc7911792015-07-30 17:55:30 -0700135 if (notification.getCause() == RemovalCause.EXPIRED) {
136 providerService.meterOperationFailed(notification.getValue(),
137 MeterFailReason.TIMEOUT);
138 }
139 }).build();
140
141 controller.addEventListener(listener);
142 controller.addListener(listener);
143
144 controller.getSwitches().forEach((sw -> createStatsCollection(sw)));
145 }
146
147 @Deactivate
148 public void deactivate() {
149 providerRegistry.unregister(this);
Charles Chanecfdfb72015-11-24 19:05:50 -0800150 collectors.values().forEach(MeterStatsCollector::stop);
151 collectors.clear();
alshabibc7911792015-07-30 17:55:30 -0700152 controller.removeEventListener(listener);
153 controller.removeListener(listener);
154 providerService = null;
155 }
156
alshabibbc371962015-07-09 22:26:21 -0700157 @Override
158 public void performMeterOperation(DeviceId deviceId, MeterOperations meterOps) {
alshabibc7911792015-07-30 17:55:30 -0700159 Dpid dpid = Dpid.dpid(deviceId.uri());
160 OpenFlowSwitch sw = controller.getSwitch(dpid);
161 if (sw == null) {
162 log.error("Unknown device {}", deviceId);
163 meterOps.operations().forEach(op ->
164 providerService.meterOperationFailed(op,
165 MeterFailReason.UNKNOWN_DEVICE)
166 );
167 return;
168 }
alshabibbc371962015-07-09 22:26:21 -0700169
alshabibc7911792015-07-30 17:55:30 -0700170 meterOps.operations().forEach(op -> performOperation(sw, op));
alshabibbc371962015-07-09 22:26:21 -0700171 }
172
173 @Override
174 public void performMeterOperation(DeviceId deviceId, MeterOperation meterOp) {
alshabibc7911792015-07-30 17:55:30 -0700175 Dpid dpid = Dpid.dpid(deviceId.uri());
176 OpenFlowSwitch sw = controller.getSwitch(dpid);
177 if (sw == null) {
178 log.error("Unknown device {}", deviceId);
179 providerService.meterOperationFailed(meterOp,
180 MeterFailReason.UNKNOWN_DEVICE);
181 return;
182 }
alshabibbc371962015-07-09 22:26:21 -0700183
alshabib7bb05012015-08-05 10:15:09 -0700184 performOperation(sw, meterOp);
185
Jordi Ortiz9fe79a22017-04-25 12:57:11 +0200186 if (meterOp.type().equals(MeterOperation.Type.REMOVE)) {
187 forceMeterStats(deviceId);
188 }
189
190 }
191
192 private void forceMeterStats(DeviceId deviceId) {
193 Dpid dpid = Dpid.dpid(deviceId.uri());
194 OpenFlowSwitch sw = controller.getSwitch(dpid);
195
196 MeterStatsCollector once = new MeterStatsCollector(sw, 1);
Palash Kalaf95c38b2017-05-15 23:52:48 +0900197 once.sendMeterStatisticRequest();
Jordi Ortiz9fe79a22017-04-25 12:57:11 +0200198
alshabibbc371962015-07-09 22:26:21 -0700199 }
alshabibc7911792015-07-30 17:55:30 -0700200
201 private void performOperation(OpenFlowSwitch sw, MeterOperation op) {
202
203 pendingOperations.put(op.meter().id().id(), op);
204
205
206 Meter meter = op.meter();
207 MeterModBuilder builder = MeterModBuilder.builder(meter.id().id(), sw.factory());
208 if (meter.isBurst()) {
209 builder.burst();
210 }
211 builder.withBands(meter.bands())
212 .withId(meter.id())
213 .withRateUnit(meter.unit());
214
215 switch (op.type()) {
216 case ADD:
217 sw.sendMsg(builder.add());
218 break;
219 case REMOVE:
220 sw.sendMsg(builder.remove());
221 break;
222 case MODIFY:
223 sw.sendMsg(builder.modify());
224 break;
225 default:
226 log.warn("Unknown Meter command {}; not sending anything",
227 op.type());
228 providerService.meterOperationFailed(op,
229 MeterFailReason.UNKNOWN_COMMAND);
230 }
231
232 }
233
234 private void createStatsCollection(OpenFlowSwitch sw) {
Madan Jampani84382b92016-06-22 08:26:49 -0700235 if (sw != null && isMeterSupported(sw)) {
alshabibc7911792015-07-30 17:55:30 -0700236 MeterStatsCollector msc = new MeterStatsCollector(sw, POLL_INTERVAL);
Thomas Vachuskaa394b952016-06-14 15:02:09 -0700237 stopCollectorIfNeeded(collectors.put(new Dpid(sw.getId()), msc));
Palash Kalaa439afe2017-05-16 14:53:15 +0900238 msc.start();
Thomas Vachuskaa394b952016-06-14 15:02:09 -0700239 }
240 }
241
242 private void stopCollectorIfNeeded(MeterStatsCollector collector) {
243 if (collector != null) {
244 collector.stop();
alshabibc7911792015-07-30 17:55:30 -0700245 }
246 }
247
Charles Chan14967c22015-12-07 11:11:50 -0800248 // TODO: ONOS-3546 Support per device enabling/disabling via network config
alshabibc7911792015-07-30 17:55:30 -0700249 private boolean isMeterSupported(OpenFlowSwitch sw) {
250 if (sw.factory().getVersion() == OFVersion.OF_10 ||
251 sw.factory().getVersion() == OFVersion.OF_11 ||
Charles Chan14967c22015-12-07 11:11:50 -0800252 sw.factory().getVersion() == OFVersion.OF_12 ||
Yuta HIGUCHI67f2cca2017-01-19 19:31:58 -0800253 NO_METER_SUPPORT.contains(sw.deviceType()) ||
Charles Chan14967c22015-12-07 11:11:50 -0800254 sw.softwareDescription().equals("OF-DPA 2.0")) {
alshabibc7911792015-07-30 17:55:30 -0700255 return false;
256 }
257
258 return true;
259 }
260
261 private void pushMeterStats(Dpid dpid, OFStatsReply msg) {
alshabib7bb05012015-08-05 10:15:09 -0700262 DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid));
alshabibc7911792015-07-30 17:55:30 -0700263
alshabib7bb05012015-08-05 10:15:09 -0700264 if (msg.getStatsType() == OFStatsType.METER) {
265 OFMeterStatsReply reply = (OFMeterStatsReply) msg;
266 Collection<Meter> meters = buildMeters(deviceId, reply.getEntries());
267 //TODO do meter accounting here.
268 providerService.pushMeterMetrics(deviceId, meters);
269 } else if (msg.getStatsType() == OFStatsType.METER_CONFIG) {
270 OFMeterConfigStatsReply reply = (OFMeterConfigStatsReply) msg;
271 // FIXME: Map<Long, Meter> meters = collectMeters(deviceId, reply);
272 }
273
274 }
275
Jordi Ortizaa8de492016-12-01 00:21:36 +0100276 private MeterFeatures buildMeterFeatures(Dpid dpid, OFMeterFeatures mf) {
277 if (mf != null) {
278 return new MeterFeaturesBuilder(mf, deviceId(uri(dpid)))
279 .build();
280 } else {
281 // This will usually happen for OpenFlow devices prior to 1.3
282 return MeterFeaturesBuilder.noMeterFeatures(deviceId(uri(dpid)));
283 }
284 }
285
286 private void pushMeterFeatures(Dpid dpid, OFMeterFeatures meterFeatures) {
287 providerService.pushMeterFeatures(deviceId(uri(dpid)),
288 buildMeterFeatures(dpid, meterFeatures));
289 }
290
291 private void destroyMeterFeatures(Dpid dpid) {
292 providerService.deleteMeterFeatures(deviceId(uri(dpid)));
293 }
294
alshabib7bb05012015-08-05 10:15:09 -0700295 private Map<Long, Meter> collectMeters(DeviceId deviceId,
296 OFMeterConfigStatsReply reply) {
297 return Maps.newHashMap();
298 //TODO: Needs a fix to be applied to loxi MeterConfig stat is incorrect
299 }
300
301 private Collection<Meter> buildMeters(DeviceId deviceId,
302 List<OFMeterStats> entries) {
303 return entries.stream().map(stat -> {
304 DefaultMeter.Builder builder = DefaultMeter.builder();
305 Collection<Band> bands = buildBands(stat.getBandStats());
306 builder.forDevice(deviceId)
alshabib58fe6dc2015-08-19 17:16:13 -0700307 .withId(MeterId.meterId(stat.getMeterId()))
alshabib7bb05012015-08-05 10:15:09 -0700308 //FIXME: need to encode appId in meter id, but that makes
309 // things a little annoying for debugging
310 .fromApp(coreService.getAppId("org.onosproject.core"))
311 .withBands(bands);
312 DefaultMeter meter = builder.build();
313 meter.setState(MeterState.ADDED);
314 meter.setLife(stat.getDurationSec());
315 meter.setProcessedBytes(stat.getByteInCount().getValue());
316 meter.setProcessedPackets(stat.getPacketInCount().getValue());
317 meter.setReferenceCount(stat.getFlowCount());
318
319 // marks the meter as seen on the dataplane
320 pendingOperations.invalidate(stat.getMeterId());
321 return meter;
322 }).collect(Collectors.toSet());
323 }
324
325 private Collection<Band> buildBands(List<OFMeterBandStats> bandStats) {
326 return bandStats.stream().map(stat -> {
327 DefaultBand band = DefaultBand.builder().build();
328 band.setBytes(stat.getByteBandCount().getValue());
329 band.setPackets(stat.getPacketBandCount().getValue());
330 return band;
331 }).collect(Collectors.toSet());
alshabibc7911792015-07-30 17:55:30 -0700332 }
333
334 private void signalMeterError(OFMeterModFailedErrorMsg meterError,
335 MeterOperation op) {
336 switch (meterError.getCode()) {
337 case UNKNOWN:
338 providerService.meterOperationFailed(op,
339 MeterFailReason.UNKNOWN_DEVICE);
340 break;
341 case METER_EXISTS:
342 providerService.meterOperationFailed(op,
343 MeterFailReason.EXISTING_METER);
344 break;
345 case INVALID_METER:
346 providerService.meterOperationFailed(op,
347 MeterFailReason.INVALID_METER);
348 break;
349 case UNKNOWN_METER:
350 providerService.meterOperationFailed(op,
351 MeterFailReason.UNKNOWN);
352 break;
353 case BAD_COMMAND:
354 providerService.meterOperationFailed(op,
355 MeterFailReason.UNKNOWN_COMMAND);
356 break;
357 case BAD_FLAGS:
358 providerService.meterOperationFailed(op,
359 MeterFailReason.UNKNOWN_FLAGS);
360 break;
361 case BAD_RATE:
362 providerService.meterOperationFailed(op,
363 MeterFailReason.BAD_RATE);
364 break;
365 case BAD_BURST:
366 providerService.meterOperationFailed(op,
367 MeterFailReason.BAD_BURST);
368 break;
369 case BAD_BAND:
370 providerService.meterOperationFailed(op,
371 MeterFailReason.BAD_BAND);
372 break;
373 case BAD_BAND_VALUE:
374 providerService.meterOperationFailed(op,
375 MeterFailReason.BAD_BAND_VALUE);
376 break;
377 case OUT_OF_METERS:
378 providerService.meterOperationFailed(op,
379 MeterFailReason.OUT_OF_METERS);
380 break;
381 case OUT_OF_BANDS:
382 providerService.meterOperationFailed(op,
383 MeterFailReason.OUT_OF_BANDS);
384 break;
385 default:
386 providerService.meterOperationFailed(op,
387 MeterFailReason.UNKNOWN);
388 }
389 }
390
391 private class InternalMeterListener
392 implements OpenFlowSwitchListener, OpenFlowEventListener {
393 @Override
394 public void handleMessage(Dpid dpid, OFMessage msg) {
395 switch (msg.getType()) {
396 case STATS_REPLY:
397 pushMeterStats(dpid, (OFStatsReply) msg);
398 break;
399 case ERROR:
400 OFErrorMsg error = (OFErrorMsg) msg;
401 if (error.getErrType() == OFErrorType.METER_MOD_FAILED) {
402 MeterOperation op =
403 pendingOperations.getIfPresent(error.getXid());
404 pendingOperations.invalidate(error.getXid());
405 if (op == null) {
406 log.warn("Unknown Meter operation failed {}", error);
407 } else {
408 OFMeterModFailedErrorMsg meterError =
409 (OFMeterModFailedErrorMsg) error;
410 signalMeterError(meterError, op);
411 }
412 }
413 break;
414 default:
415 break;
416 }
417
418 }
419
420 @Override
421 public void switchAdded(Dpid dpid) {
422 createStatsCollection(controller.getSwitch(dpid));
Jordi Ortizaa8de492016-12-01 00:21:36 +0100423 pushMeterFeatures(dpid, controller.getSwitch(dpid).getMeterFeatures());
alshabibc7911792015-07-30 17:55:30 -0700424 }
425
426 @Override
427 public void switchRemoved(Dpid dpid) {
Thomas Vachuskaa394b952016-06-14 15:02:09 -0700428 stopCollectorIfNeeded(collectors.remove(dpid));
Jordi Ortizaa8de492016-12-01 00:21:36 +0100429 destroyMeterFeatures(dpid);
alshabibc7911792015-07-30 17:55:30 -0700430 }
431
432 @Override
433 public void switchChanged(Dpid dpid) {
434
435 }
436
437 @Override
438 public void portChanged(Dpid dpid, OFPortStatus status) {
439
440 }
441
442 @Override
443 public void receivedRoleReply(Dpid dpid, RoleState requested, RoleState response) {
444
445 }
446 }
447
448
449
alshabibe27055b2015-07-09 21:43:10 -0700450}