blob: b343c565f5b16378d3c397d8e9d035e2df16fd11 [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
alshabibbc371962015-07-09 22:26:21 -0700186 }
alshabibc7911792015-07-30 17:55:30 -0700187
188 private void performOperation(OpenFlowSwitch sw, MeterOperation op) {
189
190 pendingOperations.put(op.meter().id().id(), op);
191
192
193 Meter meter = op.meter();
194 MeterModBuilder builder = MeterModBuilder.builder(meter.id().id(), sw.factory());
195 if (meter.isBurst()) {
196 builder.burst();
197 }
198 builder.withBands(meter.bands())
199 .withId(meter.id())
200 .withRateUnit(meter.unit());
201
202 switch (op.type()) {
203 case ADD:
204 sw.sendMsg(builder.add());
205 break;
206 case REMOVE:
207 sw.sendMsg(builder.remove());
208 break;
209 case MODIFY:
210 sw.sendMsg(builder.modify());
211 break;
212 default:
213 log.warn("Unknown Meter command {}; not sending anything",
214 op.type());
215 providerService.meterOperationFailed(op,
216 MeterFailReason.UNKNOWN_COMMAND);
217 }
218
219 }
220
221 private void createStatsCollection(OpenFlowSwitch sw) {
Madan Jampani84382b92016-06-22 08:26:49 -0700222 if (sw != null && isMeterSupported(sw)) {
alshabibc7911792015-07-30 17:55:30 -0700223 MeterStatsCollector msc = new MeterStatsCollector(sw, POLL_INTERVAL);
224 msc.start();
Thomas Vachuskaa394b952016-06-14 15:02:09 -0700225 stopCollectorIfNeeded(collectors.put(new Dpid(sw.getId()), msc));
226 }
227 }
228
229 private void stopCollectorIfNeeded(MeterStatsCollector collector) {
230 if (collector != null) {
231 collector.stop();
alshabibc7911792015-07-30 17:55:30 -0700232 }
233 }
234
Charles Chan14967c22015-12-07 11:11:50 -0800235 // TODO: ONOS-3546 Support per device enabling/disabling via network config
alshabibc7911792015-07-30 17:55:30 -0700236 private boolean isMeterSupported(OpenFlowSwitch sw) {
237 if (sw.factory().getVersion() == OFVersion.OF_10 ||
238 sw.factory().getVersion() == OFVersion.OF_11 ||
Charles Chan14967c22015-12-07 11:11:50 -0800239 sw.factory().getVersion() == OFVersion.OF_12 ||
Yuta HIGUCHI67f2cca2017-01-19 19:31:58 -0800240 NO_METER_SUPPORT.contains(sw.deviceType()) ||
Charles Chan14967c22015-12-07 11:11:50 -0800241 sw.softwareDescription().equals("OF-DPA 2.0")) {
alshabibc7911792015-07-30 17:55:30 -0700242 return false;
243 }
244
245 return true;
246 }
247
248 private void pushMeterStats(Dpid dpid, OFStatsReply msg) {
alshabib7bb05012015-08-05 10:15:09 -0700249 DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid));
alshabibc7911792015-07-30 17:55:30 -0700250
alshabib7bb05012015-08-05 10:15:09 -0700251 if (msg.getStatsType() == OFStatsType.METER) {
252 OFMeterStatsReply reply = (OFMeterStatsReply) msg;
253 Collection<Meter> meters = buildMeters(deviceId, reply.getEntries());
254 //TODO do meter accounting here.
255 providerService.pushMeterMetrics(deviceId, meters);
256 } else if (msg.getStatsType() == OFStatsType.METER_CONFIG) {
257 OFMeterConfigStatsReply reply = (OFMeterConfigStatsReply) msg;
258 // FIXME: Map<Long, Meter> meters = collectMeters(deviceId, reply);
259 }
260
261 }
262
Jordi Ortizaa8de492016-12-01 00:21:36 +0100263 private MeterFeatures buildMeterFeatures(Dpid dpid, OFMeterFeatures mf) {
264 if (mf != null) {
265 return new MeterFeaturesBuilder(mf, deviceId(uri(dpid)))
266 .build();
267 } else {
268 // This will usually happen for OpenFlow devices prior to 1.3
269 return MeterFeaturesBuilder.noMeterFeatures(deviceId(uri(dpid)));
270 }
271 }
272
273 private void pushMeterFeatures(Dpid dpid, OFMeterFeatures meterFeatures) {
274 providerService.pushMeterFeatures(deviceId(uri(dpid)),
275 buildMeterFeatures(dpid, meterFeatures));
276 }
277
278 private void destroyMeterFeatures(Dpid dpid) {
279 providerService.deleteMeterFeatures(deviceId(uri(dpid)));
280 }
281
alshabib7bb05012015-08-05 10:15:09 -0700282 private Map<Long, Meter> collectMeters(DeviceId deviceId,
283 OFMeterConfigStatsReply reply) {
284 return Maps.newHashMap();
285 //TODO: Needs a fix to be applied to loxi MeterConfig stat is incorrect
286 }
287
288 private Collection<Meter> buildMeters(DeviceId deviceId,
289 List<OFMeterStats> entries) {
290 return entries.stream().map(stat -> {
291 DefaultMeter.Builder builder = DefaultMeter.builder();
292 Collection<Band> bands = buildBands(stat.getBandStats());
293 builder.forDevice(deviceId)
alshabib58fe6dc2015-08-19 17:16:13 -0700294 .withId(MeterId.meterId(stat.getMeterId()))
alshabib7bb05012015-08-05 10:15:09 -0700295 //FIXME: need to encode appId in meter id, but that makes
296 // things a little annoying for debugging
297 .fromApp(coreService.getAppId("org.onosproject.core"))
298 .withBands(bands);
299 DefaultMeter meter = builder.build();
300 meter.setState(MeterState.ADDED);
301 meter.setLife(stat.getDurationSec());
302 meter.setProcessedBytes(stat.getByteInCount().getValue());
303 meter.setProcessedPackets(stat.getPacketInCount().getValue());
304 meter.setReferenceCount(stat.getFlowCount());
305
306 // marks the meter as seen on the dataplane
307 pendingOperations.invalidate(stat.getMeterId());
308 return meter;
309 }).collect(Collectors.toSet());
310 }
311
312 private Collection<Band> buildBands(List<OFMeterBandStats> bandStats) {
313 return bandStats.stream().map(stat -> {
314 DefaultBand band = DefaultBand.builder().build();
315 band.setBytes(stat.getByteBandCount().getValue());
316 band.setPackets(stat.getPacketBandCount().getValue());
317 return band;
318 }).collect(Collectors.toSet());
alshabibc7911792015-07-30 17:55:30 -0700319 }
320
321 private void signalMeterError(OFMeterModFailedErrorMsg meterError,
322 MeterOperation op) {
323 switch (meterError.getCode()) {
324 case UNKNOWN:
325 providerService.meterOperationFailed(op,
326 MeterFailReason.UNKNOWN_DEVICE);
327 break;
328 case METER_EXISTS:
329 providerService.meterOperationFailed(op,
330 MeterFailReason.EXISTING_METER);
331 break;
332 case INVALID_METER:
333 providerService.meterOperationFailed(op,
334 MeterFailReason.INVALID_METER);
335 break;
336 case UNKNOWN_METER:
337 providerService.meterOperationFailed(op,
338 MeterFailReason.UNKNOWN);
339 break;
340 case BAD_COMMAND:
341 providerService.meterOperationFailed(op,
342 MeterFailReason.UNKNOWN_COMMAND);
343 break;
344 case BAD_FLAGS:
345 providerService.meterOperationFailed(op,
346 MeterFailReason.UNKNOWN_FLAGS);
347 break;
348 case BAD_RATE:
349 providerService.meterOperationFailed(op,
350 MeterFailReason.BAD_RATE);
351 break;
352 case BAD_BURST:
353 providerService.meterOperationFailed(op,
354 MeterFailReason.BAD_BURST);
355 break;
356 case BAD_BAND:
357 providerService.meterOperationFailed(op,
358 MeterFailReason.BAD_BAND);
359 break;
360 case BAD_BAND_VALUE:
361 providerService.meterOperationFailed(op,
362 MeterFailReason.BAD_BAND_VALUE);
363 break;
364 case OUT_OF_METERS:
365 providerService.meterOperationFailed(op,
366 MeterFailReason.OUT_OF_METERS);
367 break;
368 case OUT_OF_BANDS:
369 providerService.meterOperationFailed(op,
370 MeterFailReason.OUT_OF_BANDS);
371 break;
372 default:
373 providerService.meterOperationFailed(op,
374 MeterFailReason.UNKNOWN);
375 }
376 }
377
378 private class InternalMeterListener
379 implements OpenFlowSwitchListener, OpenFlowEventListener {
380 @Override
381 public void handleMessage(Dpid dpid, OFMessage msg) {
382 switch (msg.getType()) {
383 case STATS_REPLY:
384 pushMeterStats(dpid, (OFStatsReply) msg);
385 break;
386 case ERROR:
387 OFErrorMsg error = (OFErrorMsg) msg;
388 if (error.getErrType() == OFErrorType.METER_MOD_FAILED) {
389 MeterOperation op =
390 pendingOperations.getIfPresent(error.getXid());
391 pendingOperations.invalidate(error.getXid());
392 if (op == null) {
393 log.warn("Unknown Meter operation failed {}", error);
394 } else {
395 OFMeterModFailedErrorMsg meterError =
396 (OFMeterModFailedErrorMsg) error;
397 signalMeterError(meterError, op);
398 }
399 }
400 break;
401 default:
402 break;
403 }
404
405 }
406
407 @Override
408 public void switchAdded(Dpid dpid) {
409 createStatsCollection(controller.getSwitch(dpid));
Jordi Ortizaa8de492016-12-01 00:21:36 +0100410 pushMeterFeatures(dpid, controller.getSwitch(dpid).getMeterFeatures());
alshabibc7911792015-07-30 17:55:30 -0700411 }
412
413 @Override
414 public void switchRemoved(Dpid dpid) {
Thomas Vachuskaa394b952016-06-14 15:02:09 -0700415 stopCollectorIfNeeded(collectors.remove(dpid));
Jordi Ortizaa8de492016-12-01 00:21:36 +0100416 destroyMeterFeatures(dpid);
alshabibc7911792015-07-30 17:55:30 -0700417 }
418
419 @Override
420 public void switchChanged(Dpid dpid) {
421
422 }
423
424 @Override
425 public void portChanged(Dpid dpid, OFPortStatus status) {
426
427 }
428
429 @Override
430 public void receivedRoleReply(Dpid dpid, RoleState requested, RoleState response) {
431
432 }
433 }
434
435
436
alshabibe27055b2015-07-09 21:43:10 -0700437}