blob: cff47bb1d76f363b43126ee69911f1bc33fd67a4 [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;
Yuta HIGUCHI67f2cca2017-01-19 19:31:58 -080024import com.google.common.collect.ImmutableSet;
alshabibc7911792015-07-30 17:55:30 -070025import com.google.common.collect.Maps;
26import org.apache.felix.scr.annotations.Activate;
alshabibe27055b2015-07-09 21:43:10 -070027import org.apache.felix.scr.annotations.Component;
alshabibc7911792015-07-30 17:55:30 -070028import org.apache.felix.scr.annotations.Deactivate;
alshabibe27055b2015-07-09 21:43:10 -070029import org.apache.felix.scr.annotations.Reference;
30import org.apache.felix.scr.annotations.ReferenceCardinality;
alshabib7bb05012015-08-05 10:15:09 -070031import org.onosproject.core.CoreService;
alshabib10c810b2015-08-18 16:59:04 -070032import org.onosproject.net.meter.Band;
33import org.onosproject.net.meter.DefaultBand;
34import org.onosproject.net.meter.DefaultMeter;
35import org.onosproject.net.meter.Meter;
36import org.onosproject.net.meter.MeterFailReason;
alshabib58fe6dc2015-08-19 17:16:13 -070037import org.onosproject.net.meter.MeterId;
alshabib10c810b2015-08-18 16:59:04 -070038import org.onosproject.net.meter.MeterOperation;
39import org.onosproject.net.meter.MeterOperations;
40import org.onosproject.net.meter.MeterProvider;
41import org.onosproject.net.meter.MeterProviderRegistry;
42import org.onosproject.net.meter.MeterProviderService;
43import org.onosproject.net.meter.MeterState;
Yuta HIGUCHI67f2cca2017-01-19 19:31:58 -080044import org.onosproject.net.Device;
alshabib7bb05012015-08-05 10:15:09 -070045import org.onosproject.net.DeviceId;
alshabibe27055b2015-07-09 21:43:10 -070046import org.onosproject.net.provider.AbstractProvider;
47import org.onosproject.net.provider.ProviderId;
alshabibc7911792015-07-30 17:55:30 -070048import org.onosproject.openflow.controller.Dpid;
alshabibe27055b2015-07-09 21:43:10 -070049import org.onosproject.openflow.controller.OpenFlowController;
alshabibc7911792015-07-30 17:55:30 -070050import org.onosproject.openflow.controller.OpenFlowEventListener;
51import org.onosproject.openflow.controller.OpenFlowSwitch;
52import org.onosproject.openflow.controller.OpenFlowSwitchListener;
53import org.onosproject.openflow.controller.RoleState;
54import org.projectfloodlight.openflow.protocol.OFErrorMsg;
55import org.projectfloodlight.openflow.protocol.OFErrorType;
56import org.projectfloodlight.openflow.protocol.OFMessage;
alshabib7bb05012015-08-05 10:15:09 -070057import org.projectfloodlight.openflow.protocol.OFMeterBandStats;
58import org.projectfloodlight.openflow.protocol.OFMeterConfigStatsReply;
59import org.projectfloodlight.openflow.protocol.OFMeterStats;
60import org.projectfloodlight.openflow.protocol.OFMeterStatsReply;
alshabibc7911792015-07-30 17:55:30 -070061import org.projectfloodlight.openflow.protocol.OFPortStatus;
62import org.projectfloodlight.openflow.protocol.OFStatsReply;
alshabib7bb05012015-08-05 10:15:09 -070063import org.projectfloodlight.openflow.protocol.OFStatsType;
alshabibc7911792015-07-30 17:55:30 -070064import org.projectfloodlight.openflow.protocol.OFVersion;
65import org.projectfloodlight.openflow.protocol.errormsg.OFMeterModFailedErrorMsg;
alshabibe27055b2015-07-09 21:43:10 -070066import org.slf4j.Logger;
67
alshabib7bb05012015-08-05 10:15:09 -070068import java.util.Collection;
Yuta HIGUCHI67f2cca2017-01-19 19:31:58 -080069import java.util.EnumSet;
alshabib7bb05012015-08-05 10:15:09 -070070import java.util.List;
alshabibc7911792015-07-30 17:55:30 -070071import java.util.Map;
Yuta HIGUCHI67f2cca2017-01-19 19:31:58 -080072import java.util.Set;
alshabibc7911792015-07-30 17:55:30 -070073import java.util.concurrent.TimeUnit;
74import java.util.concurrent.atomic.AtomicLong;
alshabib7bb05012015-08-05 10:15:09 -070075import java.util.stream.Collectors;
alshabibc7911792015-07-30 17:55:30 -070076
alshabibe27055b2015-07-09 21:43:10 -070077import static org.slf4j.LoggerFactory.getLogger;
78
79/**
80 * Provider which uses an OpenFlow controller to handle meters.
81 */
alshabib58fe6dc2015-08-19 17:16:13 -070082@Component(immediate = true, enabled = true)
alshabibe27055b2015-07-09 21:43:10 -070083public class OpenFlowMeterProvider extends AbstractProvider implements MeterProvider {
84
alshabibc7911792015-07-30 17:55:30 -070085
alshabibe27055b2015-07-09 21:43:10 -070086 private final Logger log = getLogger(getClass());
87
88 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected OpenFlowController controller;
90
alshabibc7911792015-07-30 17:55:30 -070091 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92 protected MeterProviderRegistry providerRegistry;
93
alshabib7bb05012015-08-05 10:15:09 -070094 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected CoreService coreService;
96
alshabibc7911792015-07-30 17:55:30 -070097 private MeterProviderService providerService;
98
99 private static final AtomicLong XID_COUNTER = new AtomicLong(1);
100
101 static final int POLL_INTERVAL = 10;
102 static final long TIMEOUT = 30;
103
alshabib7bb05012015-08-05 10:15:09 -0700104 private Cache<Long, MeterOperation> pendingOperations;
alshabibc7911792015-07-30 17:55:30 -0700105
106
107 private InternalMeterListener listener = new InternalMeterListener();
108 private Map<Dpid, MeterStatsCollector> collectors = Maps.newHashMap();
alshabibe27055b2015-07-09 21:43:10 -0700109
Yuta HIGUCHI67f2cca2017-01-19 19:31:58 -0800110 private static final Set<Device.Type> NO_METER_SUPPORT =
111 ImmutableSet.copyOf(EnumSet.of(Device.Type.ROADM,
112 Device.Type.ROADM_OTN,
113 Device.Type.FIBER_SWITCH,
114 Device.Type.OTN));
115
alshabibe27055b2015-07-09 21:43:10 -0700116 /**
117 * Creates a OpenFlow meter provider.
118 */
119 public OpenFlowMeterProvider() {
120 super(new ProviderId("of", "org.onosproject.provider.meter"));
121 }
alshabibbc371962015-07-09 22:26:21 -0700122
alshabibc7911792015-07-30 17:55:30 -0700123 @Activate
124 public void activate() {
125 providerService = providerRegistry.register(this);
126
127 pendingOperations = CacheBuilder.newBuilder()
128 .expireAfterWrite(TIMEOUT, TimeUnit.SECONDS)
alshabib7bb05012015-08-05 10:15:09 -0700129 .removalListener((RemovalNotification<Long, MeterOperation> notification) -> {
alshabibc7911792015-07-30 17:55:30 -0700130 if (notification.getCause() == RemovalCause.EXPIRED) {
131 providerService.meterOperationFailed(notification.getValue(),
132 MeterFailReason.TIMEOUT);
133 }
134 }).build();
135
136 controller.addEventListener(listener);
137 controller.addListener(listener);
138
139 controller.getSwitches().forEach((sw -> createStatsCollection(sw)));
140 }
141
142 @Deactivate
143 public void deactivate() {
144 providerRegistry.unregister(this);
Charles Chanecfdfb72015-11-24 19:05:50 -0800145 collectors.values().forEach(MeterStatsCollector::stop);
146 collectors.clear();
alshabibc7911792015-07-30 17:55:30 -0700147 controller.removeEventListener(listener);
148 controller.removeListener(listener);
149 providerService = null;
150 }
151
alshabibbc371962015-07-09 22:26:21 -0700152 @Override
153 public void performMeterOperation(DeviceId deviceId, MeterOperations meterOps) {
alshabibc7911792015-07-30 17:55:30 -0700154 Dpid dpid = Dpid.dpid(deviceId.uri());
155 OpenFlowSwitch sw = controller.getSwitch(dpid);
156 if (sw == null) {
157 log.error("Unknown device {}", deviceId);
158 meterOps.operations().forEach(op ->
159 providerService.meterOperationFailed(op,
160 MeterFailReason.UNKNOWN_DEVICE)
161 );
162 return;
163 }
alshabibbc371962015-07-09 22:26:21 -0700164
alshabibc7911792015-07-30 17:55:30 -0700165 meterOps.operations().forEach(op -> performOperation(sw, op));
alshabibbc371962015-07-09 22:26:21 -0700166 }
167
168 @Override
169 public void performMeterOperation(DeviceId deviceId, MeterOperation meterOp) {
alshabibc7911792015-07-30 17:55:30 -0700170 Dpid dpid = Dpid.dpid(deviceId.uri());
171 OpenFlowSwitch sw = controller.getSwitch(dpid);
172 if (sw == null) {
173 log.error("Unknown device {}", deviceId);
174 providerService.meterOperationFailed(meterOp,
175 MeterFailReason.UNKNOWN_DEVICE);
176 return;
177 }
alshabibbc371962015-07-09 22:26:21 -0700178
alshabib7bb05012015-08-05 10:15:09 -0700179 performOperation(sw, meterOp);
180
alshabibbc371962015-07-09 22:26:21 -0700181 }
alshabibc7911792015-07-30 17:55:30 -0700182
183 private void performOperation(OpenFlowSwitch sw, MeterOperation op) {
184
185 pendingOperations.put(op.meter().id().id(), op);
186
187
188 Meter meter = op.meter();
189 MeterModBuilder builder = MeterModBuilder.builder(meter.id().id(), sw.factory());
190 if (meter.isBurst()) {
191 builder.burst();
192 }
193 builder.withBands(meter.bands())
194 .withId(meter.id())
195 .withRateUnit(meter.unit());
196
197 switch (op.type()) {
198 case ADD:
199 sw.sendMsg(builder.add());
200 break;
201 case REMOVE:
202 sw.sendMsg(builder.remove());
203 break;
204 case MODIFY:
205 sw.sendMsg(builder.modify());
206 break;
207 default:
208 log.warn("Unknown Meter command {}; not sending anything",
209 op.type());
210 providerService.meterOperationFailed(op,
211 MeterFailReason.UNKNOWN_COMMAND);
212 }
213
214 }
215
216 private void createStatsCollection(OpenFlowSwitch sw) {
Madan Jampani84382b92016-06-22 08:26:49 -0700217 if (sw != null && isMeterSupported(sw)) {
alshabibc7911792015-07-30 17:55:30 -0700218 MeterStatsCollector msc = new MeterStatsCollector(sw, POLL_INTERVAL);
219 msc.start();
Thomas Vachuskaa394b952016-06-14 15:02:09 -0700220 stopCollectorIfNeeded(collectors.put(new Dpid(sw.getId()), msc));
221 }
222 }
223
224 private void stopCollectorIfNeeded(MeterStatsCollector collector) {
225 if (collector != null) {
226 collector.stop();
alshabibc7911792015-07-30 17:55:30 -0700227 }
228 }
229
Charles Chan14967c22015-12-07 11:11:50 -0800230 // TODO: ONOS-3546 Support per device enabling/disabling via network config
alshabibc7911792015-07-30 17:55:30 -0700231 private boolean isMeterSupported(OpenFlowSwitch sw) {
232 if (sw.factory().getVersion() == OFVersion.OF_10 ||
233 sw.factory().getVersion() == OFVersion.OF_11 ||
Charles Chan14967c22015-12-07 11:11:50 -0800234 sw.factory().getVersion() == OFVersion.OF_12 ||
Yuta HIGUCHI67f2cca2017-01-19 19:31:58 -0800235 NO_METER_SUPPORT.contains(sw.deviceType()) ||
Charles Chan14967c22015-12-07 11:11:50 -0800236 sw.softwareDescription().equals("OF-DPA 2.0")) {
alshabibc7911792015-07-30 17:55:30 -0700237 return false;
238 }
239
240 return true;
241 }
242
243 private void pushMeterStats(Dpid dpid, OFStatsReply msg) {
alshabib7bb05012015-08-05 10:15:09 -0700244 DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid));
alshabibc7911792015-07-30 17:55:30 -0700245
alshabib7bb05012015-08-05 10:15:09 -0700246 if (msg.getStatsType() == OFStatsType.METER) {
247 OFMeterStatsReply reply = (OFMeterStatsReply) msg;
248 Collection<Meter> meters = buildMeters(deviceId, reply.getEntries());
249 //TODO do meter accounting here.
250 providerService.pushMeterMetrics(deviceId, meters);
251 } else if (msg.getStatsType() == OFStatsType.METER_CONFIG) {
252 OFMeterConfigStatsReply reply = (OFMeterConfigStatsReply) msg;
253 // FIXME: Map<Long, Meter> meters = collectMeters(deviceId, reply);
254 }
255
256 }
257
258 private Map<Long, Meter> collectMeters(DeviceId deviceId,
259 OFMeterConfigStatsReply reply) {
260 return Maps.newHashMap();
261 //TODO: Needs a fix to be applied to loxi MeterConfig stat is incorrect
262 }
263
264 private Collection<Meter> buildMeters(DeviceId deviceId,
265 List<OFMeterStats> entries) {
266 return entries.stream().map(stat -> {
267 DefaultMeter.Builder builder = DefaultMeter.builder();
268 Collection<Band> bands = buildBands(stat.getBandStats());
269 builder.forDevice(deviceId)
alshabib58fe6dc2015-08-19 17:16:13 -0700270 .withId(MeterId.meterId(stat.getMeterId()))
alshabib7bb05012015-08-05 10:15:09 -0700271 //FIXME: need to encode appId in meter id, but that makes
272 // things a little annoying for debugging
273 .fromApp(coreService.getAppId("org.onosproject.core"))
274 .withBands(bands);
275 DefaultMeter meter = builder.build();
276 meter.setState(MeterState.ADDED);
277 meter.setLife(stat.getDurationSec());
278 meter.setProcessedBytes(stat.getByteInCount().getValue());
279 meter.setProcessedPackets(stat.getPacketInCount().getValue());
280 meter.setReferenceCount(stat.getFlowCount());
281
282 // marks the meter as seen on the dataplane
283 pendingOperations.invalidate(stat.getMeterId());
284 return meter;
285 }).collect(Collectors.toSet());
286 }
287
288 private Collection<Band> buildBands(List<OFMeterBandStats> bandStats) {
289 return bandStats.stream().map(stat -> {
290 DefaultBand band = DefaultBand.builder().build();
291 band.setBytes(stat.getByteBandCount().getValue());
292 band.setPackets(stat.getPacketBandCount().getValue());
293 return band;
294 }).collect(Collectors.toSet());
alshabibc7911792015-07-30 17:55:30 -0700295 }
296
297 private void signalMeterError(OFMeterModFailedErrorMsg meterError,
298 MeterOperation op) {
299 switch (meterError.getCode()) {
300 case UNKNOWN:
301 providerService.meterOperationFailed(op,
302 MeterFailReason.UNKNOWN_DEVICE);
303 break;
304 case METER_EXISTS:
305 providerService.meterOperationFailed(op,
306 MeterFailReason.EXISTING_METER);
307 break;
308 case INVALID_METER:
309 providerService.meterOperationFailed(op,
310 MeterFailReason.INVALID_METER);
311 break;
312 case UNKNOWN_METER:
313 providerService.meterOperationFailed(op,
314 MeterFailReason.UNKNOWN);
315 break;
316 case BAD_COMMAND:
317 providerService.meterOperationFailed(op,
318 MeterFailReason.UNKNOWN_COMMAND);
319 break;
320 case BAD_FLAGS:
321 providerService.meterOperationFailed(op,
322 MeterFailReason.UNKNOWN_FLAGS);
323 break;
324 case BAD_RATE:
325 providerService.meterOperationFailed(op,
326 MeterFailReason.BAD_RATE);
327 break;
328 case BAD_BURST:
329 providerService.meterOperationFailed(op,
330 MeterFailReason.BAD_BURST);
331 break;
332 case BAD_BAND:
333 providerService.meterOperationFailed(op,
334 MeterFailReason.BAD_BAND);
335 break;
336 case BAD_BAND_VALUE:
337 providerService.meterOperationFailed(op,
338 MeterFailReason.BAD_BAND_VALUE);
339 break;
340 case OUT_OF_METERS:
341 providerService.meterOperationFailed(op,
342 MeterFailReason.OUT_OF_METERS);
343 break;
344 case OUT_OF_BANDS:
345 providerService.meterOperationFailed(op,
346 MeterFailReason.OUT_OF_BANDS);
347 break;
348 default:
349 providerService.meterOperationFailed(op,
350 MeterFailReason.UNKNOWN);
351 }
352 }
353
354 private class InternalMeterListener
355 implements OpenFlowSwitchListener, OpenFlowEventListener {
356 @Override
357 public void handleMessage(Dpid dpid, OFMessage msg) {
358 switch (msg.getType()) {
359 case STATS_REPLY:
360 pushMeterStats(dpid, (OFStatsReply) msg);
361 break;
362 case ERROR:
363 OFErrorMsg error = (OFErrorMsg) msg;
364 if (error.getErrType() == OFErrorType.METER_MOD_FAILED) {
365 MeterOperation op =
366 pendingOperations.getIfPresent(error.getXid());
367 pendingOperations.invalidate(error.getXid());
368 if (op == null) {
369 log.warn("Unknown Meter operation failed {}", error);
370 } else {
371 OFMeterModFailedErrorMsg meterError =
372 (OFMeterModFailedErrorMsg) error;
373 signalMeterError(meterError, op);
374 }
375 }
376 break;
377 default:
378 break;
379 }
380
381 }
382
383 @Override
384 public void switchAdded(Dpid dpid) {
385 createStatsCollection(controller.getSwitch(dpid));
386 }
387
388 @Override
389 public void switchRemoved(Dpid dpid) {
Thomas Vachuskaa394b952016-06-14 15:02:09 -0700390 stopCollectorIfNeeded(collectors.remove(dpid));
alshabibc7911792015-07-30 17:55:30 -0700391 }
392
393 @Override
394 public void switchChanged(Dpid dpid) {
395
396 }
397
398 @Override
399 public void portChanged(Dpid dpid, OFPortStatus status) {
400
401 }
402
403 @Override
404 public void receivedRoleReply(Dpid dpid, RoleState requested, RoleState response) {
405
406 }
407 }
408
409
410
alshabibe27055b2015-07-09 21:43:10 -0700411}