blob: f5a777be8e4542bdf1378f7ed72eb264334b68ce [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);
135 controller.removeEventListener(listener);
136 controller.removeListener(listener);
137 providerService = null;
138 }
139
alshabibbc371962015-07-09 22:26:21 -0700140 @Override
141 public void performMeterOperation(DeviceId deviceId, MeterOperations meterOps) {
alshabibc7911792015-07-30 17:55:30 -0700142 Dpid dpid = Dpid.dpid(deviceId.uri());
143 OpenFlowSwitch sw = controller.getSwitch(dpid);
144 if (sw == null) {
145 log.error("Unknown device {}", deviceId);
146 meterOps.operations().forEach(op ->
147 providerService.meterOperationFailed(op,
148 MeterFailReason.UNKNOWN_DEVICE)
149 );
150 return;
151 }
alshabibbc371962015-07-09 22:26:21 -0700152
alshabibc7911792015-07-30 17:55:30 -0700153 meterOps.operations().forEach(op -> performOperation(sw, op));
alshabibbc371962015-07-09 22:26:21 -0700154 }
155
156 @Override
157 public void performMeterOperation(DeviceId deviceId, MeterOperation meterOp) {
alshabibc7911792015-07-30 17:55:30 -0700158 Dpid dpid = Dpid.dpid(deviceId.uri());
159 OpenFlowSwitch sw = controller.getSwitch(dpid);
160 if (sw == null) {
161 log.error("Unknown device {}", deviceId);
162 providerService.meterOperationFailed(meterOp,
163 MeterFailReason.UNKNOWN_DEVICE);
164 return;
165 }
alshabibbc371962015-07-09 22:26:21 -0700166
alshabib7bb05012015-08-05 10:15:09 -0700167 performOperation(sw, meterOp);
168
alshabibbc371962015-07-09 22:26:21 -0700169 }
alshabibc7911792015-07-30 17:55:30 -0700170
171 private void performOperation(OpenFlowSwitch sw, MeterOperation op) {
172
173 pendingOperations.put(op.meter().id().id(), op);
174
175
176 Meter meter = op.meter();
177 MeterModBuilder builder = MeterModBuilder.builder(meter.id().id(), sw.factory());
178 if (meter.isBurst()) {
179 builder.burst();
180 }
181 builder.withBands(meter.bands())
182 .withId(meter.id())
183 .withRateUnit(meter.unit());
184
185 switch (op.type()) {
186 case ADD:
187 sw.sendMsg(builder.add());
188 break;
189 case REMOVE:
190 sw.sendMsg(builder.remove());
191 break;
192 case MODIFY:
193 sw.sendMsg(builder.modify());
194 break;
195 default:
196 log.warn("Unknown Meter command {}; not sending anything",
197 op.type());
198 providerService.meterOperationFailed(op,
199 MeterFailReason.UNKNOWN_COMMAND);
200 }
201
202 }
203
204 private void createStatsCollection(OpenFlowSwitch sw) {
205 if (isMeterSupported(sw)) {
206 MeterStatsCollector msc = new MeterStatsCollector(sw, POLL_INTERVAL);
207 msc.start();
208 collectors.put(new Dpid(sw.getId()), msc);
209 }
210 }
211
212 private boolean isMeterSupported(OpenFlowSwitch sw) {
213 if (sw.factory().getVersion() == OFVersion.OF_10 ||
214 sw.factory().getVersion() == OFVersion.OF_11 ||
215 sw.factory().getVersion() == OFVersion.OF_12) {
216 return false;
217 }
218
219 return true;
220 }
221
222 private void pushMeterStats(Dpid dpid, OFStatsReply msg) {
alshabib7bb05012015-08-05 10:15:09 -0700223 DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid));
alshabibc7911792015-07-30 17:55:30 -0700224
alshabib7bb05012015-08-05 10:15:09 -0700225 if (msg.getStatsType() == OFStatsType.METER) {
226 OFMeterStatsReply reply = (OFMeterStatsReply) msg;
227 Collection<Meter> meters = buildMeters(deviceId, reply.getEntries());
228 //TODO do meter accounting here.
229 providerService.pushMeterMetrics(deviceId, meters);
230 } else if (msg.getStatsType() == OFStatsType.METER_CONFIG) {
231 OFMeterConfigStatsReply reply = (OFMeterConfigStatsReply) msg;
232 // FIXME: Map<Long, Meter> meters = collectMeters(deviceId, reply);
233 }
234
235 }
236
237 private Map<Long, Meter> collectMeters(DeviceId deviceId,
238 OFMeterConfigStatsReply reply) {
239 return Maps.newHashMap();
240 //TODO: Needs a fix to be applied to loxi MeterConfig stat is incorrect
241 }
242
243 private Collection<Meter> buildMeters(DeviceId deviceId,
244 List<OFMeterStats> entries) {
245 return entries.stream().map(stat -> {
246 DefaultMeter.Builder builder = DefaultMeter.builder();
247 Collection<Band> bands = buildBands(stat.getBandStats());
248 builder.forDevice(deviceId)
alshabib58fe6dc2015-08-19 17:16:13 -0700249 .withId(MeterId.meterId(stat.getMeterId()))
alshabib7bb05012015-08-05 10:15:09 -0700250 //FIXME: need to encode appId in meter id, but that makes
251 // things a little annoying for debugging
252 .fromApp(coreService.getAppId("org.onosproject.core"))
253 .withBands(bands);
254 DefaultMeter meter = builder.build();
255 meter.setState(MeterState.ADDED);
256 meter.setLife(stat.getDurationSec());
257 meter.setProcessedBytes(stat.getByteInCount().getValue());
258 meter.setProcessedPackets(stat.getPacketInCount().getValue());
259 meter.setReferenceCount(stat.getFlowCount());
260
261 // marks the meter as seen on the dataplane
262 pendingOperations.invalidate(stat.getMeterId());
263 return meter;
264 }).collect(Collectors.toSet());
265 }
266
267 private Collection<Band> buildBands(List<OFMeterBandStats> bandStats) {
268 return bandStats.stream().map(stat -> {
269 DefaultBand band = DefaultBand.builder().build();
270 band.setBytes(stat.getByteBandCount().getValue());
271 band.setPackets(stat.getPacketBandCount().getValue());
272 return band;
273 }).collect(Collectors.toSet());
alshabibc7911792015-07-30 17:55:30 -0700274 }
275
276 private void signalMeterError(OFMeterModFailedErrorMsg meterError,
277 MeterOperation op) {
278 switch (meterError.getCode()) {
279 case UNKNOWN:
280 providerService.meterOperationFailed(op,
281 MeterFailReason.UNKNOWN_DEVICE);
282 break;
283 case METER_EXISTS:
284 providerService.meterOperationFailed(op,
285 MeterFailReason.EXISTING_METER);
286 break;
287 case INVALID_METER:
288 providerService.meterOperationFailed(op,
289 MeterFailReason.INVALID_METER);
290 break;
291 case UNKNOWN_METER:
292 providerService.meterOperationFailed(op,
293 MeterFailReason.UNKNOWN);
294 break;
295 case BAD_COMMAND:
296 providerService.meterOperationFailed(op,
297 MeterFailReason.UNKNOWN_COMMAND);
298 break;
299 case BAD_FLAGS:
300 providerService.meterOperationFailed(op,
301 MeterFailReason.UNKNOWN_FLAGS);
302 break;
303 case BAD_RATE:
304 providerService.meterOperationFailed(op,
305 MeterFailReason.BAD_RATE);
306 break;
307 case BAD_BURST:
308 providerService.meterOperationFailed(op,
309 MeterFailReason.BAD_BURST);
310 break;
311 case BAD_BAND:
312 providerService.meterOperationFailed(op,
313 MeterFailReason.BAD_BAND);
314 break;
315 case BAD_BAND_VALUE:
316 providerService.meterOperationFailed(op,
317 MeterFailReason.BAD_BAND_VALUE);
318 break;
319 case OUT_OF_METERS:
320 providerService.meterOperationFailed(op,
321 MeterFailReason.OUT_OF_METERS);
322 break;
323 case OUT_OF_BANDS:
324 providerService.meterOperationFailed(op,
325 MeterFailReason.OUT_OF_BANDS);
326 break;
327 default:
328 providerService.meterOperationFailed(op,
329 MeterFailReason.UNKNOWN);
330 }
331 }
332
333 private class InternalMeterListener
334 implements OpenFlowSwitchListener, OpenFlowEventListener {
335 @Override
336 public void handleMessage(Dpid dpid, OFMessage msg) {
337 switch (msg.getType()) {
338 case STATS_REPLY:
339 pushMeterStats(dpid, (OFStatsReply) msg);
340 break;
341 case ERROR:
342 OFErrorMsg error = (OFErrorMsg) msg;
343 if (error.getErrType() == OFErrorType.METER_MOD_FAILED) {
344 MeterOperation op =
345 pendingOperations.getIfPresent(error.getXid());
346 pendingOperations.invalidate(error.getXid());
347 if (op == null) {
348 log.warn("Unknown Meter operation failed {}", error);
349 } else {
350 OFMeterModFailedErrorMsg meterError =
351 (OFMeterModFailedErrorMsg) error;
352 signalMeterError(meterError, op);
353 }
354 }
355 break;
356 default:
357 break;
358 }
359
360 }
361
362 @Override
363 public void switchAdded(Dpid dpid) {
364 createStatsCollection(controller.getSwitch(dpid));
365 }
366
367 @Override
368 public void switchRemoved(Dpid dpid) {
369 MeterStatsCollector msc = collectors.remove(dpid);
370 if (msc != null) {
371 msc.stop();
372 }
373 }
374
375 @Override
376 public void switchChanged(Dpid dpid) {
377
378 }
379
380 @Override
381 public void portChanged(Dpid dpid, OFPortStatus status) {
382
383 }
384
385 @Override
386 public void receivedRoleReply(Dpid dpid, RoleState requested, RoleState response) {
387
388 }
389 }
390
391
392
alshabibe27055b2015-07-09 21:43:10 -0700393}