f5a777be8e4542bdf1378f7ed72eb264334b68ce
[onosfw.git] /
1 /*
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
17 package org.onosproject.provider.of.meter.impl;
18
19
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;
65
66 import java.util.Collection;
67 import java.util.List;
68 import java.util.Map;
69 import java.util.concurrent.TimeUnit;
70 import java.util.concurrent.atomic.AtomicLong;
71 import java.util.stream.Collectors;
72
73 import static org.slf4j.LoggerFactory.getLogger;
74
75 /**
76  * Provider which uses an OpenFlow controller to handle meters.
77  */
78 @Component(immediate = true, enabled = true)
79 public class OpenFlowMeterProvider extends AbstractProvider implements MeterProvider {
80
81
82     private final Logger log = getLogger(getClass());
83
84     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
85     protected OpenFlowController controller;
86
87     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
88     protected MeterProviderRegistry providerRegistry;
89
90     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
91     protected CoreService coreService;
92
93     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
100     private Cache<Long, MeterOperation> pendingOperations;
101
102
103     private InternalMeterListener listener = new InternalMeterListener();
104     private Map<Dpid, MeterStatsCollector> collectors = Maps.newHashMap();
105
106     /**
107      * Creates a OpenFlow meter provider.
108      */
109     public OpenFlowMeterProvider() {
110         super(new ProviderId("of", "org.onosproject.provider.meter"));
111     }
112
113     @Activate
114     public void activate() {
115         providerService = providerRegistry.register(this);
116
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);
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
140     @Override
141     public void performMeterOperation(DeviceId deviceId, MeterOperations meterOps) {
142         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         }
152
153         meterOps.operations().forEach(op -> performOperation(sw, op));
154     }
155
156     @Override
157     public void performMeterOperation(DeviceId deviceId, MeterOperation meterOp) {
158         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         }
166
167         performOperation(sw, meterOp);
168
169     }
170
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) {
223         DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid));
224
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);
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)
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"))
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());
274     }
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
393 }