blob: e56a54c00a4ba2183b0a3c282b9d7b7f1d9ca3e6 [file] [log] [blame]
alshabibe27055b2015-07-09 21:43:10 -07001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
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;
31import org.onosproject.incubator.net.meter.Band;
32import org.onosproject.incubator.net.meter.DefaultBand;
33import org.onosproject.incubator.net.meter.DefaultMeter;
alshabib1d2bc402015-07-31 17:04:11 -070034import org.onosproject.incubator.net.meter.Meter;
35import org.onosproject.incubator.net.meter.MeterFailReason;
36import org.onosproject.incubator.net.meter.MeterOperation;
37import org.onosproject.incubator.net.meter.MeterOperations;
38import org.onosproject.incubator.net.meter.MeterProvider;
39import org.onosproject.incubator.net.meter.MeterProviderRegistry;
40import org.onosproject.incubator.net.meter.MeterProviderService;
alshabib7bb05012015-08-05 10:15:09 -070041import org.onosproject.incubator.net.meter.MeterState;
42import org.onosproject.net.DeviceId;
alshabibe27055b2015-07-09 21:43:10 -070043import org.onosproject.net.provider.AbstractProvider;
44import org.onosproject.net.provider.ProviderId;
alshabibc7911792015-07-30 17:55:30 -070045import org.onosproject.openflow.controller.Dpid;
alshabibe27055b2015-07-09 21:43:10 -070046import org.onosproject.openflow.controller.OpenFlowController;
alshabibc7911792015-07-30 17:55:30 -070047import org.onosproject.openflow.controller.OpenFlowEventListener;
48import org.onosproject.openflow.controller.OpenFlowSwitch;
49import org.onosproject.openflow.controller.OpenFlowSwitchListener;
50import org.onosproject.openflow.controller.RoleState;
51import org.projectfloodlight.openflow.protocol.OFErrorMsg;
52import org.projectfloodlight.openflow.protocol.OFErrorType;
53import org.projectfloodlight.openflow.protocol.OFMessage;
alshabib7bb05012015-08-05 10:15:09 -070054import org.projectfloodlight.openflow.protocol.OFMeterBandStats;
55import org.projectfloodlight.openflow.protocol.OFMeterConfigStatsReply;
56import org.projectfloodlight.openflow.protocol.OFMeterStats;
57import org.projectfloodlight.openflow.protocol.OFMeterStatsReply;
alshabibc7911792015-07-30 17:55:30 -070058import org.projectfloodlight.openflow.protocol.OFPortStatus;
59import org.projectfloodlight.openflow.protocol.OFStatsReply;
alshabib7bb05012015-08-05 10:15:09 -070060import org.projectfloodlight.openflow.protocol.OFStatsType;
alshabibc7911792015-07-30 17:55:30 -070061import org.projectfloodlight.openflow.protocol.OFVersion;
62import org.projectfloodlight.openflow.protocol.errormsg.OFMeterModFailedErrorMsg;
alshabibe27055b2015-07-09 21:43:10 -070063import org.slf4j.Logger;
64
alshabib7bb05012015-08-05 10:15:09 -070065import java.util.Collection;
66import java.util.List;
alshabibc7911792015-07-30 17:55:30 -070067import java.util.Map;
68import java.util.concurrent.TimeUnit;
69import java.util.concurrent.atomic.AtomicLong;
alshabib7bb05012015-08-05 10:15:09 -070070import java.util.stream.Collectors;
alshabibc7911792015-07-30 17:55:30 -070071
alshabibe27055b2015-07-09 21:43:10 -070072import static org.slf4j.LoggerFactory.getLogger;
73
74/**
75 * Provider which uses an OpenFlow controller to handle meters.
76 */
Thomas Vachuskac87348d2015-07-31 12:33:50 -070077@Component(immediate = true, enabled = false)
alshabibe27055b2015-07-09 21:43:10 -070078public class OpenFlowMeterProvider extends AbstractProvider implements MeterProvider {
79
alshabibc7911792015-07-30 17:55:30 -070080
alshabibe27055b2015-07-09 21:43:10 -070081 private final Logger log = getLogger(getClass());
82
83 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
84 protected OpenFlowController controller;
85
alshabibc7911792015-07-30 17:55:30 -070086 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
87 protected MeterProviderRegistry providerRegistry;
88
alshabib7bb05012015-08-05 10:15:09 -070089 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 protected CoreService coreService;
91
alshabibc7911792015-07-30 17:55:30 -070092 private MeterProviderService providerService;
93
94 private static final AtomicLong XID_COUNTER = new AtomicLong(1);
95
96 static final int POLL_INTERVAL = 10;
97 static final long TIMEOUT = 30;
98
alshabib7bb05012015-08-05 10:15:09 -070099 private Cache<Long, MeterOperation> pendingOperations;
alshabibc7911792015-07-30 17:55:30 -0700100
101
102 private InternalMeterListener listener = new InternalMeterListener();
103 private Map<Dpid, MeterStatsCollector> collectors = Maps.newHashMap();
alshabibe27055b2015-07-09 21:43:10 -0700104
105 /**
106 * Creates a OpenFlow meter provider.
107 */
108 public OpenFlowMeterProvider() {
109 super(new ProviderId("of", "org.onosproject.provider.meter"));
110 }
alshabibbc371962015-07-09 22:26:21 -0700111
alshabibc7911792015-07-30 17:55:30 -0700112 @Activate
113 public void activate() {
114 providerService = providerRegistry.register(this);
115
116 pendingOperations = CacheBuilder.newBuilder()
117 .expireAfterWrite(TIMEOUT, TimeUnit.SECONDS)
alshabib7bb05012015-08-05 10:15:09 -0700118 .removalListener((RemovalNotification<Long, MeterOperation> notification) -> {
alshabibc7911792015-07-30 17:55:30 -0700119 if (notification.getCause() == RemovalCause.EXPIRED) {
120 providerService.meterOperationFailed(notification.getValue(),
121 MeterFailReason.TIMEOUT);
122 }
123 }).build();
124
125 controller.addEventListener(listener);
126 controller.addListener(listener);
127
128 controller.getSwitches().forEach((sw -> createStatsCollection(sw)));
129 }
130
131 @Deactivate
132 public void deactivate() {
133 providerRegistry.unregister(this);
134 controller.removeEventListener(listener);
135 controller.removeListener(listener);
136 providerService = null;
137 }
138
alshabibbc371962015-07-09 22:26:21 -0700139 @Override
140 public void performMeterOperation(DeviceId deviceId, MeterOperations meterOps) {
alshabibc7911792015-07-30 17:55:30 -0700141 Dpid dpid = Dpid.dpid(deviceId.uri());
142 OpenFlowSwitch sw = controller.getSwitch(dpid);
143 if (sw == null) {
144 log.error("Unknown device {}", deviceId);
145 meterOps.operations().forEach(op ->
146 providerService.meterOperationFailed(op,
147 MeterFailReason.UNKNOWN_DEVICE)
148 );
149 return;
150 }
alshabibbc371962015-07-09 22:26:21 -0700151
alshabibc7911792015-07-30 17:55:30 -0700152 meterOps.operations().forEach(op -> performOperation(sw, op));
alshabibbc371962015-07-09 22:26:21 -0700153 }
154
155 @Override
156 public void performMeterOperation(DeviceId deviceId, MeterOperation meterOp) {
alshabibc7911792015-07-30 17:55:30 -0700157 Dpid dpid = Dpid.dpid(deviceId.uri());
158 OpenFlowSwitch sw = controller.getSwitch(dpid);
159 if (sw == null) {
160 log.error("Unknown device {}", deviceId);
161 providerService.meterOperationFailed(meterOp,
162 MeterFailReason.UNKNOWN_DEVICE);
163 return;
164 }
alshabibbc371962015-07-09 22:26:21 -0700165
alshabib7bb05012015-08-05 10:15:09 -0700166 performOperation(sw, meterOp);
167
alshabibbc371962015-07-09 22:26:21 -0700168 }
alshabibc7911792015-07-30 17:55:30 -0700169
170 private void performOperation(OpenFlowSwitch sw, MeterOperation op) {
171
172 pendingOperations.put(op.meter().id().id(), op);
173
174
175 Meter meter = op.meter();
176 MeterModBuilder builder = MeterModBuilder.builder(meter.id().id(), sw.factory());
177 if (meter.isBurst()) {
178 builder.burst();
179 }
180 builder.withBands(meter.bands())
181 .withId(meter.id())
182 .withRateUnit(meter.unit());
183
184 switch (op.type()) {
185 case ADD:
186 sw.sendMsg(builder.add());
187 break;
188 case REMOVE:
189 sw.sendMsg(builder.remove());
190 break;
191 case MODIFY:
192 sw.sendMsg(builder.modify());
193 break;
194 default:
195 log.warn("Unknown Meter command {}; not sending anything",
196 op.type());
197 providerService.meterOperationFailed(op,
198 MeterFailReason.UNKNOWN_COMMAND);
199 }
200
201 }
202
203 private void createStatsCollection(OpenFlowSwitch sw) {
204 if (isMeterSupported(sw)) {
205 MeterStatsCollector msc = new MeterStatsCollector(sw, POLL_INTERVAL);
206 msc.start();
207 collectors.put(new Dpid(sw.getId()), msc);
208 }
209 }
210
211 private boolean isMeterSupported(OpenFlowSwitch sw) {
212 if (sw.factory().getVersion() == OFVersion.OF_10 ||
213 sw.factory().getVersion() == OFVersion.OF_11 ||
214 sw.factory().getVersion() == OFVersion.OF_12) {
215 return false;
216 }
217
218 return true;
219 }
220
221 private void pushMeterStats(Dpid dpid, OFStatsReply msg) {
alshabib7bb05012015-08-05 10:15:09 -0700222 DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid));
alshabibc7911792015-07-30 17:55:30 -0700223
alshabib7bb05012015-08-05 10:15:09 -0700224 if (msg.getStatsType() == OFStatsType.METER) {
225 OFMeterStatsReply reply = (OFMeterStatsReply) msg;
226 Collection<Meter> meters = buildMeters(deviceId, reply.getEntries());
227 //TODO do meter accounting here.
228 providerService.pushMeterMetrics(deviceId, meters);
229 } else if (msg.getStatsType() == OFStatsType.METER_CONFIG) {
230 OFMeterConfigStatsReply reply = (OFMeterConfigStatsReply) msg;
231 // FIXME: Map<Long, Meter> meters = collectMeters(deviceId, reply);
232 }
233
234 }
235
236 private Map<Long, Meter> collectMeters(DeviceId deviceId,
237 OFMeterConfigStatsReply reply) {
238 return Maps.newHashMap();
239 //TODO: Needs a fix to be applied to loxi MeterConfig stat is incorrect
240 }
241
242 private Collection<Meter> buildMeters(DeviceId deviceId,
243 List<OFMeterStats> entries) {
244 return entries.stream().map(stat -> {
245 DefaultMeter.Builder builder = DefaultMeter.builder();
246 Collection<Band> bands = buildBands(stat.getBandStats());
247 builder.forDevice(deviceId)
248 .withId(stat.getMeterId())
249 //FIXME: need to encode appId in meter id, but that makes
250 // things a little annoying for debugging
251 .fromApp(coreService.getAppId("org.onosproject.core"))
252 .withBands(bands);
253 DefaultMeter meter = builder.build();
254 meter.setState(MeterState.ADDED);
255 meter.setLife(stat.getDurationSec());
256 meter.setProcessedBytes(stat.getByteInCount().getValue());
257 meter.setProcessedPackets(stat.getPacketInCount().getValue());
258 meter.setReferenceCount(stat.getFlowCount());
259
260 // marks the meter as seen on the dataplane
261 pendingOperations.invalidate(stat.getMeterId());
262 return meter;
263 }).collect(Collectors.toSet());
264 }
265
266 private Collection<Band> buildBands(List<OFMeterBandStats> bandStats) {
267 return bandStats.stream().map(stat -> {
268 DefaultBand band = DefaultBand.builder().build();
269 band.setBytes(stat.getByteBandCount().getValue());
270 band.setPackets(stat.getPacketBandCount().getValue());
271 return band;
272 }).collect(Collectors.toSet());
alshabibc7911792015-07-30 17:55:30 -0700273 }
274
275 private void signalMeterError(OFMeterModFailedErrorMsg meterError,
276 MeterOperation op) {
277 switch (meterError.getCode()) {
278 case UNKNOWN:
279 providerService.meterOperationFailed(op,
280 MeterFailReason.UNKNOWN_DEVICE);
281 break;
282 case METER_EXISTS:
283 providerService.meterOperationFailed(op,
284 MeterFailReason.EXISTING_METER);
285 break;
286 case INVALID_METER:
287 providerService.meterOperationFailed(op,
288 MeterFailReason.INVALID_METER);
289 break;
290 case UNKNOWN_METER:
291 providerService.meterOperationFailed(op,
292 MeterFailReason.UNKNOWN);
293 break;
294 case BAD_COMMAND:
295 providerService.meterOperationFailed(op,
296 MeterFailReason.UNKNOWN_COMMAND);
297 break;
298 case BAD_FLAGS:
299 providerService.meterOperationFailed(op,
300 MeterFailReason.UNKNOWN_FLAGS);
301 break;
302 case BAD_RATE:
303 providerService.meterOperationFailed(op,
304 MeterFailReason.BAD_RATE);
305 break;
306 case BAD_BURST:
307 providerService.meterOperationFailed(op,
308 MeterFailReason.BAD_BURST);
309 break;
310 case BAD_BAND:
311 providerService.meterOperationFailed(op,
312 MeterFailReason.BAD_BAND);
313 break;
314 case BAD_BAND_VALUE:
315 providerService.meterOperationFailed(op,
316 MeterFailReason.BAD_BAND_VALUE);
317 break;
318 case OUT_OF_METERS:
319 providerService.meterOperationFailed(op,
320 MeterFailReason.OUT_OF_METERS);
321 break;
322 case OUT_OF_BANDS:
323 providerService.meterOperationFailed(op,
324 MeterFailReason.OUT_OF_BANDS);
325 break;
326 default:
327 providerService.meterOperationFailed(op,
328 MeterFailReason.UNKNOWN);
329 }
330 }
331
332 private class InternalMeterListener
333 implements OpenFlowSwitchListener, OpenFlowEventListener {
334 @Override
335 public void handleMessage(Dpid dpid, OFMessage msg) {
336 switch (msg.getType()) {
337 case STATS_REPLY:
338 pushMeterStats(dpid, (OFStatsReply) msg);
339 break;
340 case ERROR:
341 OFErrorMsg error = (OFErrorMsg) msg;
342 if (error.getErrType() == OFErrorType.METER_MOD_FAILED) {
343 MeterOperation op =
344 pendingOperations.getIfPresent(error.getXid());
345 pendingOperations.invalidate(error.getXid());
346 if (op == null) {
347 log.warn("Unknown Meter operation failed {}", error);
348 } else {
349 OFMeterModFailedErrorMsg meterError =
350 (OFMeterModFailedErrorMsg) error;
351 signalMeterError(meterError, op);
352 }
353 }
354 break;
355 default:
356 break;
357 }
358
359 }
360
361 @Override
362 public void switchAdded(Dpid dpid) {
363 createStatsCollection(controller.getSwitch(dpid));
364 }
365
366 @Override
367 public void switchRemoved(Dpid dpid) {
368 MeterStatsCollector msc = collectors.remove(dpid);
369 if (msc != null) {
370 msc.stop();
371 }
372 }
373
374 @Override
375 public void switchChanged(Dpid dpid) {
376
377 }
378
379 @Override
380 public void portChanged(Dpid dpid, OFPortStatus status) {
381
382 }
383
384 @Override
385 public void receivedRoleReply(Dpid dpid, RoleState requested, RoleState response) {
386
387 }
388 }
389
390
391
alshabibe27055b2015-07-09 21:43:10 -0700392}