blob: e41137902a0687712737716acaaffe9c7c0d7956 [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;
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) {
207 if (isMeterSupported(sw)) {
208 MeterStatsCollector msc = new MeterStatsCollector(sw, POLL_INTERVAL);
209 msc.start();
210 collectors.put(new Dpid(sw.getId()), msc);
211 }
212 }
213
214 private boolean isMeterSupported(OpenFlowSwitch sw) {
215 if (sw.factory().getVersion() == OFVersion.OF_10 ||
216 sw.factory().getVersion() == OFVersion.OF_11 ||
217 sw.factory().getVersion() == OFVersion.OF_12) {
218 return false;
219 }
220
221 return true;
222 }
223
224 private void pushMeterStats(Dpid dpid, OFStatsReply msg) {
alshabib7bb05012015-08-05 10:15:09 -0700225 DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid));
alshabibc7911792015-07-30 17:55:30 -0700226
alshabib7bb05012015-08-05 10:15:09 -0700227 if (msg.getStatsType() == OFStatsType.METER) {
228 OFMeterStatsReply reply = (OFMeterStatsReply) msg;
229 Collection<Meter> meters = buildMeters(deviceId, reply.getEntries());
230 //TODO do meter accounting here.
231 providerService.pushMeterMetrics(deviceId, meters);
232 } else if (msg.getStatsType() == OFStatsType.METER_CONFIG) {
233 OFMeterConfigStatsReply reply = (OFMeterConfigStatsReply) msg;
234 // FIXME: Map<Long, Meter> meters = collectMeters(deviceId, reply);
235 }
236
237 }
238
239 private Map<Long, Meter> collectMeters(DeviceId deviceId,
240 OFMeterConfigStatsReply reply) {
241 return Maps.newHashMap();
242 //TODO: Needs a fix to be applied to loxi MeterConfig stat is incorrect
243 }
244
245 private Collection<Meter> buildMeters(DeviceId deviceId,
246 List<OFMeterStats> entries) {
247 return entries.stream().map(stat -> {
248 DefaultMeter.Builder builder = DefaultMeter.builder();
249 Collection<Band> bands = buildBands(stat.getBandStats());
250 builder.forDevice(deviceId)
alshabib58fe6dc2015-08-19 17:16:13 -0700251 .withId(MeterId.meterId(stat.getMeterId()))
alshabib7bb05012015-08-05 10:15:09 -0700252 //FIXME: need to encode appId in meter id, but that makes
253 // things a little annoying for debugging
254 .fromApp(coreService.getAppId("org.onosproject.core"))
255 .withBands(bands);
256 DefaultMeter meter = builder.build();
257 meter.setState(MeterState.ADDED);
258 meter.setLife(stat.getDurationSec());
259 meter.setProcessedBytes(stat.getByteInCount().getValue());
260 meter.setProcessedPackets(stat.getPacketInCount().getValue());
261 meter.setReferenceCount(stat.getFlowCount());
262
263 // marks the meter as seen on the dataplane
264 pendingOperations.invalidate(stat.getMeterId());
265 return meter;
266 }).collect(Collectors.toSet());
267 }
268
269 private Collection<Band> buildBands(List<OFMeterBandStats> bandStats) {
270 return bandStats.stream().map(stat -> {
271 DefaultBand band = DefaultBand.builder().build();
272 band.setBytes(stat.getByteBandCount().getValue());
273 band.setPackets(stat.getPacketBandCount().getValue());
274 return band;
275 }).collect(Collectors.toSet());
alshabibc7911792015-07-30 17:55:30 -0700276 }
277
278 private void signalMeterError(OFMeterModFailedErrorMsg meterError,
279 MeterOperation op) {
280 switch (meterError.getCode()) {
281 case UNKNOWN:
282 providerService.meterOperationFailed(op,
283 MeterFailReason.UNKNOWN_DEVICE);
284 break;
285 case METER_EXISTS:
286 providerService.meterOperationFailed(op,
287 MeterFailReason.EXISTING_METER);
288 break;
289 case INVALID_METER:
290 providerService.meterOperationFailed(op,
291 MeterFailReason.INVALID_METER);
292 break;
293 case UNKNOWN_METER:
294 providerService.meterOperationFailed(op,
295 MeterFailReason.UNKNOWN);
296 break;
297 case BAD_COMMAND:
298 providerService.meterOperationFailed(op,
299 MeterFailReason.UNKNOWN_COMMAND);
300 break;
301 case BAD_FLAGS:
302 providerService.meterOperationFailed(op,
303 MeterFailReason.UNKNOWN_FLAGS);
304 break;
305 case BAD_RATE:
306 providerService.meterOperationFailed(op,
307 MeterFailReason.BAD_RATE);
308 break;
309 case BAD_BURST:
310 providerService.meterOperationFailed(op,
311 MeterFailReason.BAD_BURST);
312 break;
313 case BAD_BAND:
314 providerService.meterOperationFailed(op,
315 MeterFailReason.BAD_BAND);
316 break;
317 case BAD_BAND_VALUE:
318 providerService.meterOperationFailed(op,
319 MeterFailReason.BAD_BAND_VALUE);
320 break;
321 case OUT_OF_METERS:
322 providerService.meterOperationFailed(op,
323 MeterFailReason.OUT_OF_METERS);
324 break;
325 case OUT_OF_BANDS:
326 providerService.meterOperationFailed(op,
327 MeterFailReason.OUT_OF_BANDS);
328 break;
329 default:
330 providerService.meterOperationFailed(op,
331 MeterFailReason.UNKNOWN);
332 }
333 }
334
335 private class InternalMeterListener
336 implements OpenFlowSwitchListener, OpenFlowEventListener {
337 @Override
338 public void handleMessage(Dpid dpid, OFMessage msg) {
339 switch (msg.getType()) {
340 case STATS_REPLY:
341 pushMeterStats(dpid, (OFStatsReply) msg);
342 break;
343 case ERROR:
344 OFErrorMsg error = (OFErrorMsg) msg;
345 if (error.getErrType() == OFErrorType.METER_MOD_FAILED) {
346 MeterOperation op =
347 pendingOperations.getIfPresent(error.getXid());
348 pendingOperations.invalidate(error.getXid());
349 if (op == null) {
350 log.warn("Unknown Meter operation failed {}", error);
351 } else {
352 OFMeterModFailedErrorMsg meterError =
353 (OFMeterModFailedErrorMsg) error;
354 signalMeterError(meterError, op);
355 }
356 }
357 break;
358 default:
359 break;
360 }
361
362 }
363
364 @Override
365 public void switchAdded(Dpid dpid) {
366 createStatsCollection(controller.getSwitch(dpid));
367 }
368
369 @Override
370 public void switchRemoved(Dpid dpid) {
371 MeterStatsCollector msc = collectors.remove(dpid);
372 if (msc != null) {
373 msc.stop();
374 }
375 }
376
377 @Override
378 public void switchChanged(Dpid dpid) {
379
380 }
381
382 @Override
383 public void portChanged(Dpid dpid, OFPortStatus status) {
384
385 }
386
387 @Override
388 public void receivedRoleReply(Dpid dpid, RoleState requested, RoleState response) {
389
390 }
391 }
392
393
394
alshabibe27055b2015-07-09 21:43:10 -0700395}