blob: 436b071db3be49f7f4a0294ca959ef8c57c7881b [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;
24import com.google.common.collect.Maps;
25import org.apache.felix.scr.annotations.Activate;
alshabibe27055b2015-07-09 21:43:10 -070026import org.apache.felix.scr.annotations.Component;
alshabibc7911792015-07-30 17:55:30 -070027import org.apache.felix.scr.annotations.Deactivate;
alshabibe27055b2015-07-09 21:43:10 -070028import org.apache.felix.scr.annotations.Reference;
29import org.apache.felix.scr.annotations.ReferenceCardinality;
alshabib7bb05012015-08-05 10:15:09 -070030import org.onosproject.core.CoreService;
alshabib10c810b2015-08-18 16:59:04 -070031import org.onosproject.net.meter.Band;
32import org.onosproject.net.meter.DefaultBand;
33import org.onosproject.net.meter.DefaultMeter;
34import org.onosproject.net.meter.Meter;
35import org.onosproject.net.meter.MeterFailReason;
alshabib58fe6dc2015-08-19 17:16:13 -070036import org.onosproject.net.meter.MeterId;
alshabib10c810b2015-08-18 16:59:04 -070037import org.onosproject.net.meter.MeterOperation;
38import org.onosproject.net.meter.MeterOperations;
39import org.onosproject.net.meter.MeterProvider;
40import org.onosproject.net.meter.MeterProviderRegistry;
41import org.onosproject.net.meter.MeterProviderService;
42import org.onosproject.net.meter.MeterState;
alshabib7bb05012015-08-05 10:15:09 -070043import org.onosproject.net.DeviceId;
alshabibe27055b2015-07-09 21:43:10 -070044import org.onosproject.net.provider.AbstractProvider;
45import org.onosproject.net.provider.ProviderId;
alshabibc7911792015-07-30 17:55:30 -070046import org.onosproject.openflow.controller.Dpid;
alshabibe27055b2015-07-09 21:43:10 -070047import org.onosproject.openflow.controller.OpenFlowController;
alshabibc7911792015-07-30 17:55:30 -070048import org.onosproject.openflow.controller.OpenFlowEventListener;
49import org.onosproject.openflow.controller.OpenFlowSwitch;
50import org.onosproject.openflow.controller.OpenFlowSwitchListener;
51import org.onosproject.openflow.controller.RoleState;
52import org.projectfloodlight.openflow.protocol.OFErrorMsg;
53import org.projectfloodlight.openflow.protocol.OFErrorType;
54import org.projectfloodlight.openflow.protocol.OFMessage;
alshabib7bb05012015-08-05 10:15:09 -070055import org.projectfloodlight.openflow.protocol.OFMeterBandStats;
56import org.projectfloodlight.openflow.protocol.OFMeterConfigStatsReply;
57import org.projectfloodlight.openflow.protocol.OFMeterStats;
58import org.projectfloodlight.openflow.protocol.OFMeterStatsReply;
alshabibc7911792015-07-30 17:55:30 -070059import org.projectfloodlight.openflow.protocol.OFPortStatus;
60import org.projectfloodlight.openflow.protocol.OFStatsReply;
alshabib7bb05012015-08-05 10:15:09 -070061import org.projectfloodlight.openflow.protocol.OFStatsType;
alshabibc7911792015-07-30 17:55:30 -070062import org.projectfloodlight.openflow.protocol.OFVersion;
63import org.projectfloodlight.openflow.protocol.errormsg.OFMeterModFailedErrorMsg;
alshabibe27055b2015-07-09 21:43:10 -070064import org.slf4j.Logger;
65
alshabib7bb05012015-08-05 10:15:09 -070066import java.util.Collection;
67import java.util.List;
alshabibc7911792015-07-30 17:55:30 -070068import java.util.Map;
69import java.util.concurrent.TimeUnit;
70import java.util.concurrent.atomic.AtomicLong;
alshabib7bb05012015-08-05 10:15:09 -070071import java.util.stream.Collectors;
alshabibc7911792015-07-30 17:55:30 -070072
alshabibe27055b2015-07-09 21:43:10 -070073import static org.slf4j.LoggerFactory.getLogger;
74
75/**
76 * Provider which uses an OpenFlow controller to handle meters.
77 */
alshabib58fe6dc2015-08-19 17:16:13 -070078@Component(immediate = true, enabled = true)
alshabibe27055b2015-07-09 21:43:10 -070079public class OpenFlowMeterProvider extends AbstractProvider implements MeterProvider {
80
alshabibc7911792015-07-30 17:55:30 -070081
alshabibe27055b2015-07-09 21:43:10 -070082 private final Logger log = getLogger(getClass());
83
84 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
85 protected OpenFlowController controller;
86
alshabibc7911792015-07-30 17:55:30 -070087 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
88 protected MeterProviderRegistry providerRegistry;
89
alshabib7bb05012015-08-05 10:15:09 -070090 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
91 protected CoreService coreService;
92
alshabibc7911792015-07-30 17:55:30 -070093 private MeterProviderService providerService;
94
95 private static final AtomicLong XID_COUNTER = new AtomicLong(1);
96
97 static final int POLL_INTERVAL = 10;
98 static final long TIMEOUT = 30;
99
alshabib7bb05012015-08-05 10:15:09 -0700100 private Cache<Long, MeterOperation> pendingOperations;
alshabibc7911792015-07-30 17:55:30 -0700101
102
103 private InternalMeterListener listener = new InternalMeterListener();
104 private Map<Dpid, MeterStatsCollector> collectors = Maps.newHashMap();
alshabibe27055b2015-07-09 21:43:10 -0700105
106 /**
107 * Creates a OpenFlow meter provider.
108 */
109 public OpenFlowMeterProvider() {
110 super(new ProviderId("of", "org.onosproject.provider.meter"));
111 }
alshabibbc371962015-07-09 22:26:21 -0700112
alshabibc7911792015-07-30 17:55:30 -0700113 @Activate
114 public void activate() {
115 providerService = providerRegistry.register(this);
116
117 pendingOperations = CacheBuilder.newBuilder()
118 .expireAfterWrite(TIMEOUT, TimeUnit.SECONDS)
alshabib7bb05012015-08-05 10:15:09 -0700119 .removalListener((RemovalNotification<Long, MeterOperation> notification) -> {
alshabibc7911792015-07-30 17:55:30 -0700120 if (notification.getCause() == RemovalCause.EXPIRED) {
121 providerService.meterOperationFailed(notification.getValue(),
122 MeterFailReason.TIMEOUT);
123 }
124 }).build();
125
126 controller.addEventListener(listener);
127 controller.addListener(listener);
128
129 controller.getSwitches().forEach((sw -> createStatsCollection(sw)));
130 }
131
132 @Deactivate
133 public void deactivate() {
134 providerRegistry.unregister(this);
Charles Chanecfdfb72015-11-24 19:05:50 -0800135 collectors.values().forEach(MeterStatsCollector::stop);
136 collectors.clear();
alshabibc7911792015-07-30 17:55:30 -0700137 controller.removeEventListener(listener);
138 controller.removeListener(listener);
139 providerService = null;
140 }
141
alshabibbc371962015-07-09 22:26:21 -0700142 @Override
143 public void performMeterOperation(DeviceId deviceId, MeterOperations meterOps) {
alshabibc7911792015-07-30 17:55:30 -0700144 Dpid dpid = Dpid.dpid(deviceId.uri());
145 OpenFlowSwitch sw = controller.getSwitch(dpid);
146 if (sw == null) {
147 log.error("Unknown device {}", deviceId);
148 meterOps.operations().forEach(op ->
149 providerService.meterOperationFailed(op,
150 MeterFailReason.UNKNOWN_DEVICE)
151 );
152 return;
153 }
alshabibbc371962015-07-09 22:26:21 -0700154
alshabibc7911792015-07-30 17:55:30 -0700155 meterOps.operations().forEach(op -> performOperation(sw, op));
alshabibbc371962015-07-09 22:26:21 -0700156 }
157
158 @Override
159 public void performMeterOperation(DeviceId deviceId, MeterOperation meterOp) {
alshabibc7911792015-07-30 17:55:30 -0700160 Dpid dpid = Dpid.dpid(deviceId.uri());
161 OpenFlowSwitch sw = controller.getSwitch(dpid);
162 if (sw == null) {
163 log.error("Unknown device {}", deviceId);
164 providerService.meterOperationFailed(meterOp,
165 MeterFailReason.UNKNOWN_DEVICE);
166 return;
167 }
alshabibbc371962015-07-09 22:26:21 -0700168
alshabib7bb05012015-08-05 10:15:09 -0700169 performOperation(sw, meterOp);
170
alshabibbc371962015-07-09 22:26:21 -0700171 }
alshabibc7911792015-07-30 17:55:30 -0700172
173 private void performOperation(OpenFlowSwitch sw, MeterOperation op) {
174
175 pendingOperations.put(op.meter().id().id(), op);
176
177
178 Meter meter = op.meter();
179 MeterModBuilder builder = MeterModBuilder.builder(meter.id().id(), sw.factory());
180 if (meter.isBurst()) {
181 builder.burst();
182 }
183 builder.withBands(meter.bands())
184 .withId(meter.id())
185 .withRateUnit(meter.unit());
186
187 switch (op.type()) {
188 case ADD:
189 sw.sendMsg(builder.add());
190 break;
191 case REMOVE:
192 sw.sendMsg(builder.remove());
193 break;
194 case MODIFY:
195 sw.sendMsg(builder.modify());
196 break;
197 default:
198 log.warn("Unknown Meter command {}; not sending anything",
199 op.type());
200 providerService.meterOperationFailed(op,
201 MeterFailReason.UNKNOWN_COMMAND);
202 }
203
204 }
205
206 private void createStatsCollection(OpenFlowSwitch sw) {
Madan Jampani84382b92016-06-22 08:26:49 -0700207 if (sw != null && isMeterSupported(sw)) {
alshabibc7911792015-07-30 17:55:30 -0700208 MeterStatsCollector msc = new MeterStatsCollector(sw, POLL_INTERVAL);
209 msc.start();
Thomas Vachuskaa394b952016-06-14 15:02:09 -0700210 stopCollectorIfNeeded(collectors.put(new Dpid(sw.getId()), msc));
211 }
212 }
213
214 private void stopCollectorIfNeeded(MeterStatsCollector collector) {
215 if (collector != null) {
216 collector.stop();
alshabibc7911792015-07-30 17:55:30 -0700217 }
218 }
219
Charles Chan14967c22015-12-07 11:11:50 -0800220 // TODO: ONOS-3546 Support per device enabling/disabling via network config
alshabibc7911792015-07-30 17:55:30 -0700221 private boolean isMeterSupported(OpenFlowSwitch sw) {
222 if (sw.factory().getVersion() == OFVersion.OF_10 ||
223 sw.factory().getVersion() == OFVersion.OF_11 ||
Charles Chan14967c22015-12-07 11:11:50 -0800224 sw.factory().getVersion() == OFVersion.OF_12 ||
225 sw.softwareDescription().equals("OF-DPA 2.0")) {
alshabibc7911792015-07-30 17:55:30 -0700226 return false;
227 }
228
229 return true;
230 }
231
232 private void pushMeterStats(Dpid dpid, OFStatsReply msg) {
alshabib7bb05012015-08-05 10:15:09 -0700233 DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid));
alshabibc7911792015-07-30 17:55:30 -0700234
alshabib7bb05012015-08-05 10:15:09 -0700235 if (msg.getStatsType() == OFStatsType.METER) {
236 OFMeterStatsReply reply = (OFMeterStatsReply) msg;
237 Collection<Meter> meters = buildMeters(deviceId, reply.getEntries());
238 //TODO do meter accounting here.
239 providerService.pushMeterMetrics(deviceId, meters);
240 } else if (msg.getStatsType() == OFStatsType.METER_CONFIG) {
241 OFMeterConfigStatsReply reply = (OFMeterConfigStatsReply) msg;
242 // FIXME: Map<Long, Meter> meters = collectMeters(deviceId, reply);
243 }
244
245 }
246
247 private Map<Long, Meter> collectMeters(DeviceId deviceId,
248 OFMeterConfigStatsReply reply) {
249 return Maps.newHashMap();
250 //TODO: Needs a fix to be applied to loxi MeterConfig stat is incorrect
251 }
252
253 private Collection<Meter> buildMeters(DeviceId deviceId,
254 List<OFMeterStats> entries) {
255 return entries.stream().map(stat -> {
256 DefaultMeter.Builder builder = DefaultMeter.builder();
257 Collection<Band> bands = buildBands(stat.getBandStats());
258 builder.forDevice(deviceId)
alshabib58fe6dc2015-08-19 17:16:13 -0700259 .withId(MeterId.meterId(stat.getMeterId()))
alshabib7bb05012015-08-05 10:15:09 -0700260 //FIXME: need to encode appId in meter id, but that makes
261 // things a little annoying for debugging
262 .fromApp(coreService.getAppId("org.onosproject.core"))
263 .withBands(bands);
264 DefaultMeter meter = builder.build();
265 meter.setState(MeterState.ADDED);
266 meter.setLife(stat.getDurationSec());
267 meter.setProcessedBytes(stat.getByteInCount().getValue());
268 meter.setProcessedPackets(stat.getPacketInCount().getValue());
269 meter.setReferenceCount(stat.getFlowCount());
270
271 // marks the meter as seen on the dataplane
272 pendingOperations.invalidate(stat.getMeterId());
273 return meter;
274 }).collect(Collectors.toSet());
275 }
276
277 private Collection<Band> buildBands(List<OFMeterBandStats> bandStats) {
278 return bandStats.stream().map(stat -> {
279 DefaultBand band = DefaultBand.builder().build();
280 band.setBytes(stat.getByteBandCount().getValue());
281 band.setPackets(stat.getPacketBandCount().getValue());
282 return band;
283 }).collect(Collectors.toSet());
alshabibc7911792015-07-30 17:55:30 -0700284 }
285
286 private void signalMeterError(OFMeterModFailedErrorMsg meterError,
287 MeterOperation op) {
288 switch (meterError.getCode()) {
289 case UNKNOWN:
290 providerService.meterOperationFailed(op,
291 MeterFailReason.UNKNOWN_DEVICE);
292 break;
293 case METER_EXISTS:
294 providerService.meterOperationFailed(op,
295 MeterFailReason.EXISTING_METER);
296 break;
297 case INVALID_METER:
298 providerService.meterOperationFailed(op,
299 MeterFailReason.INVALID_METER);
300 break;
301 case UNKNOWN_METER:
302 providerService.meterOperationFailed(op,
303 MeterFailReason.UNKNOWN);
304 break;
305 case BAD_COMMAND:
306 providerService.meterOperationFailed(op,
307 MeterFailReason.UNKNOWN_COMMAND);
308 break;
309 case BAD_FLAGS:
310 providerService.meterOperationFailed(op,
311 MeterFailReason.UNKNOWN_FLAGS);
312 break;
313 case BAD_RATE:
314 providerService.meterOperationFailed(op,
315 MeterFailReason.BAD_RATE);
316 break;
317 case BAD_BURST:
318 providerService.meterOperationFailed(op,
319 MeterFailReason.BAD_BURST);
320 break;
321 case BAD_BAND:
322 providerService.meterOperationFailed(op,
323 MeterFailReason.BAD_BAND);
324 break;
325 case BAD_BAND_VALUE:
326 providerService.meterOperationFailed(op,
327 MeterFailReason.BAD_BAND_VALUE);
328 break;
329 case OUT_OF_METERS:
330 providerService.meterOperationFailed(op,
331 MeterFailReason.OUT_OF_METERS);
332 break;
333 case OUT_OF_BANDS:
334 providerService.meterOperationFailed(op,
335 MeterFailReason.OUT_OF_BANDS);
336 break;
337 default:
338 providerService.meterOperationFailed(op,
339 MeterFailReason.UNKNOWN);
340 }
341 }
342
343 private class InternalMeterListener
344 implements OpenFlowSwitchListener, OpenFlowEventListener {
345 @Override
346 public void handleMessage(Dpid dpid, OFMessage msg) {
347 switch (msg.getType()) {
348 case STATS_REPLY:
349 pushMeterStats(dpid, (OFStatsReply) msg);
350 break;
351 case ERROR:
352 OFErrorMsg error = (OFErrorMsg) msg;
353 if (error.getErrType() == OFErrorType.METER_MOD_FAILED) {
354 MeterOperation op =
355 pendingOperations.getIfPresent(error.getXid());
356 pendingOperations.invalidate(error.getXid());
357 if (op == null) {
358 log.warn("Unknown Meter operation failed {}", error);
359 } else {
360 OFMeterModFailedErrorMsg meterError =
361 (OFMeterModFailedErrorMsg) error;
362 signalMeterError(meterError, op);
363 }
364 }
365 break;
366 default:
367 break;
368 }
369
370 }
371
372 @Override
373 public void switchAdded(Dpid dpid) {
374 createStatsCollection(controller.getSwitch(dpid));
375 }
376
377 @Override
378 public void switchRemoved(Dpid dpid) {
Thomas Vachuskaa394b952016-06-14 15:02:09 -0700379 stopCollectorIfNeeded(collectors.remove(dpid));
alshabibc7911792015-07-30 17:55:30 -0700380 }
381
382 @Override
383 public void switchChanged(Dpid dpid) {
384
385 }
386
387 @Override
388 public void portChanged(Dpid dpid, OFPortStatus status) {
389
390 }
391
392 @Override
393 public void receivedRoleReply(Dpid dpid, RoleState requested, RoleState response) {
394
395 }
396 }
397
398
399
alshabibe27055b2015-07-09 21:43:10 -0700400}