2 * Copyright 2015 Open Networking Laboratory
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package org.onosproject.provider.of.meter.impl;
20 import com.google.common.cache.Cache;
21 import com.google.common.cache.CacheBuilder;
22 import com.google.common.cache.RemovalCause;
23 import com.google.common.cache.RemovalNotification;
24 import com.google.common.collect.Maps;
25 import org.apache.felix.scr.annotations.Activate;
26 import org.apache.felix.scr.annotations.Component;
27 import org.apache.felix.scr.annotations.Deactivate;
28 import org.apache.felix.scr.annotations.Reference;
29 import org.apache.felix.scr.annotations.ReferenceCardinality;
30 import org.onosproject.core.CoreService;
31 import org.onosproject.net.meter.Band;
32 import org.onosproject.net.meter.DefaultBand;
33 import org.onosproject.net.meter.DefaultMeter;
34 import org.onosproject.net.meter.Meter;
35 import org.onosproject.net.meter.MeterFailReason;
36 import org.onosproject.net.meter.MeterId;
37 import org.onosproject.net.meter.MeterOperation;
38 import org.onosproject.net.meter.MeterOperations;
39 import org.onosproject.net.meter.MeterProvider;
40 import org.onosproject.net.meter.MeterProviderRegistry;
41 import org.onosproject.net.meter.MeterProviderService;
42 import org.onosproject.net.meter.MeterState;
43 import org.onosproject.net.DeviceId;
44 import org.onosproject.net.provider.AbstractProvider;
45 import org.onosproject.net.provider.ProviderId;
46 import org.onosproject.openflow.controller.Dpid;
47 import org.onosproject.openflow.controller.OpenFlowController;
48 import org.onosproject.openflow.controller.OpenFlowEventListener;
49 import org.onosproject.openflow.controller.OpenFlowSwitch;
50 import org.onosproject.openflow.controller.OpenFlowSwitchListener;
51 import org.onosproject.openflow.controller.RoleState;
52 import org.projectfloodlight.openflow.protocol.OFErrorMsg;
53 import org.projectfloodlight.openflow.protocol.OFErrorType;
54 import org.projectfloodlight.openflow.protocol.OFMessage;
55 import org.projectfloodlight.openflow.protocol.OFMeterBandStats;
56 import org.projectfloodlight.openflow.protocol.OFMeterConfigStatsReply;
57 import org.projectfloodlight.openflow.protocol.OFMeterStats;
58 import org.projectfloodlight.openflow.protocol.OFMeterStatsReply;
59 import org.projectfloodlight.openflow.protocol.OFPortStatus;
60 import org.projectfloodlight.openflow.protocol.OFStatsReply;
61 import org.projectfloodlight.openflow.protocol.OFStatsType;
62 import org.projectfloodlight.openflow.protocol.OFVersion;
63 import org.projectfloodlight.openflow.protocol.errormsg.OFMeterModFailedErrorMsg;
64 import org.slf4j.Logger;
66 import java.util.Collection;
67 import java.util.List;
69 import java.util.concurrent.TimeUnit;
70 import java.util.concurrent.atomic.AtomicLong;
71 import java.util.stream.Collectors;
73 import static org.slf4j.LoggerFactory.getLogger;
76 * Provider which uses an OpenFlow controller to handle meters.
78 @Component(immediate = true, enabled = true)
79 public class OpenFlowMeterProvider extends AbstractProvider implements MeterProvider {
82 private final Logger log = getLogger(getClass());
84 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
85 protected OpenFlowController controller;
87 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
88 protected MeterProviderRegistry providerRegistry;
90 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
91 protected CoreService coreService;
93 private MeterProviderService providerService;
95 private static final AtomicLong XID_COUNTER = new AtomicLong(1);
97 static final int POLL_INTERVAL = 10;
98 static final long TIMEOUT = 30;
100 private Cache<Long, MeterOperation> pendingOperations;
103 private InternalMeterListener listener = new InternalMeterListener();
104 private Map<Dpid, MeterStatsCollector> collectors = Maps.newHashMap();
107 * Creates a OpenFlow meter provider.
109 public OpenFlowMeterProvider() {
110 super(new ProviderId("of", "org.onosproject.provider.meter"));
114 public void activate() {
115 providerService = providerRegistry.register(this);
117 pendingOperations = CacheBuilder.newBuilder()
118 .expireAfterWrite(TIMEOUT, TimeUnit.SECONDS)
119 .removalListener((RemovalNotification<Long, MeterOperation> notification) -> {
120 if (notification.getCause() == RemovalCause.EXPIRED) {
121 providerService.meterOperationFailed(notification.getValue(),
122 MeterFailReason.TIMEOUT);
126 controller.addEventListener(listener);
127 controller.addListener(listener);
129 controller.getSwitches().forEach((sw -> createStatsCollection(sw)));
133 public void deactivate() {
134 providerRegistry.unregister(this);
135 controller.removeEventListener(listener);
136 controller.removeListener(listener);
137 providerService = null;
141 public void performMeterOperation(DeviceId deviceId, MeterOperations meterOps) {
142 Dpid dpid = Dpid.dpid(deviceId.uri());
143 OpenFlowSwitch sw = controller.getSwitch(dpid);
145 log.error("Unknown device {}", deviceId);
146 meterOps.operations().forEach(op ->
147 providerService.meterOperationFailed(op,
148 MeterFailReason.UNKNOWN_DEVICE)
153 meterOps.operations().forEach(op -> performOperation(sw, op));
157 public void performMeterOperation(DeviceId deviceId, MeterOperation meterOp) {
158 Dpid dpid = Dpid.dpid(deviceId.uri());
159 OpenFlowSwitch sw = controller.getSwitch(dpid);
161 log.error("Unknown device {}", deviceId);
162 providerService.meterOperationFailed(meterOp,
163 MeterFailReason.UNKNOWN_DEVICE);
167 performOperation(sw, meterOp);
171 private void performOperation(OpenFlowSwitch sw, MeterOperation op) {
173 pendingOperations.put(op.meter().id().id(), op);
176 Meter meter = op.meter();
177 MeterModBuilder builder = MeterModBuilder.builder(meter.id().id(), sw.factory());
178 if (meter.isBurst()) {
181 builder.withBands(meter.bands())
183 .withRateUnit(meter.unit());
187 sw.sendMsg(builder.add());
190 sw.sendMsg(builder.remove());
193 sw.sendMsg(builder.modify());
196 log.warn("Unknown Meter command {}; not sending anything",
198 providerService.meterOperationFailed(op,
199 MeterFailReason.UNKNOWN_COMMAND);
204 private void createStatsCollection(OpenFlowSwitch sw) {
205 if (isMeterSupported(sw)) {
206 MeterStatsCollector msc = new MeterStatsCollector(sw, POLL_INTERVAL);
208 collectors.put(new Dpid(sw.getId()), msc);
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) {
222 private void pushMeterStats(Dpid dpid, OFStatsReply msg) {
223 DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid));
225 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);
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
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)
249 .withId(MeterId.meterId(stat.getMeterId()))
250 //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"))
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());
261 // marks the meter as seen on the dataplane
262 pendingOperations.invalidate(stat.getMeterId());
264 }).collect(Collectors.toSet());
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());
273 }).collect(Collectors.toSet());
276 private void signalMeterError(OFMeterModFailedErrorMsg meterError,
278 switch (meterError.getCode()) {
280 providerService.meterOperationFailed(op,
281 MeterFailReason.UNKNOWN_DEVICE);
284 providerService.meterOperationFailed(op,
285 MeterFailReason.EXISTING_METER);
288 providerService.meterOperationFailed(op,
289 MeterFailReason.INVALID_METER);
292 providerService.meterOperationFailed(op,
293 MeterFailReason.UNKNOWN);
296 providerService.meterOperationFailed(op,
297 MeterFailReason.UNKNOWN_COMMAND);
300 providerService.meterOperationFailed(op,
301 MeterFailReason.UNKNOWN_FLAGS);
304 providerService.meterOperationFailed(op,
305 MeterFailReason.BAD_RATE);
308 providerService.meterOperationFailed(op,
309 MeterFailReason.BAD_BURST);
312 providerService.meterOperationFailed(op,
313 MeterFailReason.BAD_BAND);
316 providerService.meterOperationFailed(op,
317 MeterFailReason.BAD_BAND_VALUE);
320 providerService.meterOperationFailed(op,
321 MeterFailReason.OUT_OF_METERS);
324 providerService.meterOperationFailed(op,
325 MeterFailReason.OUT_OF_BANDS);
328 providerService.meterOperationFailed(op,
329 MeterFailReason.UNKNOWN);
333 private class InternalMeterListener
334 implements OpenFlowSwitchListener, OpenFlowEventListener {
336 public void handleMessage(Dpid dpid, OFMessage msg) {
337 switch (msg.getType()) {
339 pushMeterStats(dpid, (OFStatsReply) msg);
342 OFErrorMsg error = (OFErrorMsg) msg;
343 if (error.getErrType() == OFErrorType.METER_MOD_FAILED) {
345 pendingOperations.getIfPresent(error.getXid());
346 pendingOperations.invalidate(error.getXid());
348 log.warn("Unknown Meter operation failed {}", error);
350 OFMeterModFailedErrorMsg meterError =
351 (OFMeterModFailedErrorMsg) error;
352 signalMeterError(meterError, op);
363 public void switchAdded(Dpid dpid) {
364 createStatsCollection(controller.getSwitch(dpid));
368 public void switchRemoved(Dpid dpid) {
369 MeterStatsCollector msc = collectors.remove(dpid);
376 public void switchChanged(Dpid dpid) {
381 public void portChanged(Dpid dpid, OFPortStatus status) {
386 public void receivedRoleReply(Dpid dpid, RoleState requested, RoleState response) {