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