8acf08ee96eea110100655c69fcb17bf2dd4718a
[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.group.impl;
18
19 import com.google.common.collect.Maps;
20
21 import org.apache.felix.scr.annotations.Activate;
22 import org.apache.felix.scr.annotations.Component;
23 import org.apache.felix.scr.annotations.Deactivate;
24 import org.apache.felix.scr.annotations.Reference;
25 import org.apache.felix.scr.annotations.ReferenceCardinality;
26 import org.onosproject.core.DefaultGroupId;
27 import org.onosproject.core.GroupId;
28 import org.onosproject.net.DeviceId;
29 import org.onosproject.net.group.DefaultGroup;
30 import org.onosproject.net.group.Group;
31 import org.onosproject.net.group.GroupBuckets;
32 import org.onosproject.net.group.GroupDescription;
33 import org.onosproject.net.group.GroupOperation;
34 import org.onosproject.net.group.GroupOperations;
35 import org.onosproject.net.group.GroupProvider;
36 import org.onosproject.net.group.GroupProviderRegistry;
37 import org.onosproject.net.group.GroupProviderService;
38 import org.onosproject.net.group.StoredGroupBucketEntry;
39 import org.onosproject.net.provider.AbstractProvider;
40 import org.onosproject.net.provider.ProviderId;
41 import org.onosproject.openflow.controller.Dpid;
42 import org.onosproject.openflow.controller.OpenFlowController;
43 import org.onosproject.openflow.controller.OpenFlowEventListener;
44 import org.onosproject.openflow.controller.OpenFlowSwitch;
45 import org.onosproject.openflow.controller.OpenFlowSwitchListener;
46 import org.onosproject.openflow.controller.RoleState;
47 import org.projectfloodlight.openflow.protocol.OFBucketCounter;
48 import org.projectfloodlight.openflow.protocol.OFErrorMsg;
49 import org.projectfloodlight.openflow.protocol.OFErrorType;
50 import org.projectfloodlight.openflow.protocol.OFGroupDescStatsEntry;
51 import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply;
52 import org.projectfloodlight.openflow.protocol.OFGroupMod;
53 import org.projectfloodlight.openflow.protocol.OFGroupStatsEntry;
54 import org.projectfloodlight.openflow.protocol.OFGroupStatsReply;
55 import org.projectfloodlight.openflow.protocol.OFGroupType;
56 import org.projectfloodlight.openflow.protocol.OFMessage;
57 import org.projectfloodlight.openflow.protocol.OFPortStatus;
58 import org.projectfloodlight.openflow.protocol.OFStatsReply;
59 import org.projectfloodlight.openflow.protocol.OFStatsType;
60 import org.projectfloodlight.openflow.protocol.OFVersion;
61 import org.slf4j.Logger;
62
63 import java.util.Collection;
64 import java.util.Map;
65 import java.util.Optional;
66 import java.util.concurrent.atomic.AtomicLong;
67
68 import static org.slf4j.LoggerFactory.getLogger;
69
70 /**
71  * Provider which uses an OpenFlow controller to handle Group.
72  */
73 @Component(immediate = true)
74 public class OpenFlowGroupProvider extends AbstractProvider implements GroupProvider {
75
76     private final Logger log = getLogger(getClass());
77
78     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
79     protected OpenFlowController controller;
80
81     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
82     protected GroupProviderRegistry providerRegistry;
83
84     private GroupProviderService providerService;
85
86     static final int POLL_INTERVAL = 10;
87
88     private final InternalGroupProvider listener = new InternalGroupProvider();
89
90     private static final AtomicLong XID_COUNTER = new AtomicLong(1);
91     private final Map<Dpid, GroupStatsCollector> collectors = Maps.newHashMap();
92     private final Map<Long, OFStatsReply> groupStats = Maps.newConcurrentMap();
93     private final Map<GroupId, GroupOperation> pendingGroupOperations =
94             Maps.newConcurrentMap();
95
96     /* Map<Group ID, Transaction ID> */
97     private final Map<GroupId, Long> pendingXidMaps = Maps.newConcurrentMap();
98
99     /**
100      * Creates a OpenFlow group provider.
101      */
102     public OpenFlowGroupProvider() {
103         super(new ProviderId("of", "org.onosproject.provider.group"));
104     }
105
106     @Activate
107     public void activate() {
108         providerService = providerRegistry.register(this);
109         controller.addListener(listener);
110         controller.addEventListener(listener);
111
112         for (OpenFlowSwitch sw : controller.getSwitches()) {
113             if (isGroupSupported(sw)) {
114                 GroupStatsCollector gsc = new GroupStatsCollector(sw, POLL_INTERVAL);
115                 gsc.start();
116                 collectors.put(new Dpid(sw.getId()), gsc);
117             }
118         }
119
120         log.info("Started");
121     }
122
123     @Deactivate
124     public void deactivate() {
125         providerRegistry.unregister(this);
126         providerService = null;
127
128         log.info("Stopped");
129     }
130
131     @Override
132     public void performGroupOperation(DeviceId deviceId, GroupOperations groupOps) {
133         Map<OFGroupMod, OpenFlowSwitch> mods = Maps.newIdentityHashMap();
134         final Dpid dpid = Dpid.dpid(deviceId.uri());
135         OpenFlowSwitch sw = controller.getSwitch(dpid);
136         for (GroupOperation groupOperation: groupOps.operations()) {
137             if (sw == null) {
138                 log.error("SW {} is not found", dpid);
139                 return;
140             }
141             final Long groupModXid = XID_COUNTER.getAndIncrement();
142             GroupModBuilder builder =
143                     GroupModBuilder.builder(groupOperation.buckets(),
144                             groupOperation.groupId(),
145                             groupOperation.groupType(),
146                             sw.factory(),
147                             Optional.of(groupModXid));
148             OFGroupMod groupMod = null;
149             switch (groupOperation.opType()) {
150                 case ADD:
151                     groupMod = builder.buildGroupAdd();
152                     break;
153                 case MODIFY:
154                     groupMod = builder.buildGroupMod();
155                     break;
156                 case DELETE:
157                     groupMod = builder.buildGroupDel();
158                     break;
159                 default:
160                     log.error("Unsupported Group operation");
161             }
162             sw.sendMsg(groupMod);
163             GroupId groudId = new DefaultGroupId(groupMod.getGroup().getGroupNumber());
164             pendingGroupOperations.put(groudId, groupOperation);
165             pendingXidMaps.put(groudId, groupModXid);
166         }
167      }
168
169     private void pushGroupMetrics(Dpid dpid, OFStatsReply statsReply) {
170         DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid));
171
172         OFGroupStatsReply groupStatsReply = null;
173         OFGroupDescStatsReply groupDescStatsReply = null;
174
175         synchronized (groupStats) {
176             if (statsReply.getStatsType() == OFStatsType.GROUP) {
177                 OFStatsReply reply = groupStats.get(statsReply.getXid() + 1);
178                 if (reply != null) {
179                     groupStatsReply = (OFGroupStatsReply) statsReply;
180                     groupDescStatsReply = (OFGroupDescStatsReply) reply;
181                     groupStats.remove(statsReply.getXid() + 1);
182                 } else {
183                     groupStats.put(statsReply.getXid(), statsReply);
184                 }
185             } else if (statsReply.getStatsType() == OFStatsType.GROUP_DESC) {
186                 OFStatsReply reply = groupStats.get(statsReply.getXid() - 1);
187                 if (reply != null) {
188                     groupStatsReply = (OFGroupStatsReply) reply;
189                     groupDescStatsReply = (OFGroupDescStatsReply) statsReply;
190                     groupStats.remove(statsReply.getXid() - 1);
191                 } else {
192                     groupStats.put(statsReply.getXid(), statsReply);
193                 }
194             }
195         }
196
197         if (groupStatsReply != null && groupDescStatsReply != null) {
198             Collection<Group> groups = buildGroupMetrics(deviceId,
199                     groupStatsReply, groupDescStatsReply);
200             providerService.pushGroupMetrics(deviceId, groups);
201             for (Group group: groups) {
202                 pendingGroupOperations.remove(group.id());
203                 pendingXidMaps.remove(group.id());
204             }
205         }
206     }
207
208     private Collection<Group> buildGroupMetrics(DeviceId deviceId,
209                                    OFGroupStatsReply groupStatsReply,
210                                    OFGroupDescStatsReply groupDescStatsReply) {
211
212         Map<Integer, Group> groups = Maps.newHashMap();
213
214
215         for (OFGroupDescStatsEntry entry: groupDescStatsReply.getEntries()) {
216             int id = entry.getGroup().getGroupNumber();
217             GroupId groupId = new DefaultGroupId(id);
218             GroupDescription.Type type = getGroupType(entry.getGroupType());
219             GroupBuckets buckets = new GroupBucketEntryBuilder(entry.getBuckets(),
220                     entry.getGroupType()).build();
221             DefaultGroup group = new DefaultGroup(groupId, deviceId, type, buckets);
222             groups.put(id, group);
223         }
224
225         for (OFGroupStatsEntry entry: groupStatsReply.getEntries()) {
226             int groupId = entry.getGroup().getGroupNumber();
227             DefaultGroup group = (DefaultGroup) groups.get(groupId);
228             if (group != null) {
229                 group.setBytes(entry.getByteCount().getValue());
230                 group.setLife(entry.getDurationSec());
231                 group.setPackets(entry.getPacketCount().getValue());
232                 group.setReferenceCount(entry.getRefCount());
233                 int bucketIndex = 0;
234                 for (OFBucketCounter bucketStats:entry.getBucketStats()) {
235                     ((StoredGroupBucketEntry) group.buckets().buckets()
236                             .get(bucketIndex))
237                             .setPackets(bucketStats
238                                         .getPacketCount().getValue());
239                     ((StoredGroupBucketEntry) group.buckets().buckets()
240                             .get(bucketIndex))
241                             .setBytes(entry.getBucketStats()
242                                       .get(bucketIndex)
243                                       .getByteCount().getValue());
244                     bucketIndex++;
245                 }
246             }
247         }
248
249         return groups.values();
250     }
251
252     private GroupDescription.Type getGroupType(OFGroupType type) {
253         switch (type) {
254             case ALL:
255                 return  GroupDescription.Type.ALL;
256             case INDIRECT:
257                 return GroupDescription.Type.INDIRECT;
258             case SELECT:
259                 return GroupDescription.Type.SELECT;
260             case FF:
261                 return GroupDescription.Type.FAILOVER;
262             default:
263                 log.error("Unsupported OF group type : {}", type);
264                 break;
265         }
266         return null;
267     }
268
269     /**
270      * Returns a transaction ID for entire group operations and increases
271      * the counter by the number given.
272      *
273      * @param increase the amount to increase the counter by
274      * @return a transaction ID
275      */
276     public static long getXidAndAdd(int increase) {
277         return XID_COUNTER.getAndAdd(increase);
278     }
279
280     private boolean isGroupSupported(OpenFlowSwitch sw) {
281         if (sw.factory().getVersion() == OFVersion.OF_10 ||
282                 sw.factory().getVersion() == OFVersion.OF_11 ||
283                 sw.factory().getVersion() == OFVersion.OF_12) {
284             return false;
285         }
286
287         return true;
288     }
289
290     private class InternalGroupProvider
291             implements OpenFlowSwitchListener, OpenFlowEventListener {
292
293         @Override
294         public void handleMessage(Dpid dpid, OFMessage msg) {
295             switch (msg.getType()) {
296                 case STATS_REPLY:
297                     pushGroupMetrics(dpid, (OFStatsReply) msg);
298                     break;
299                 case ERROR:
300                     OFErrorMsg errorMsg = (OFErrorMsg) msg;
301                     if (errorMsg.getErrType() == OFErrorType.GROUP_MOD_FAILED) {
302                         GroupId pendingGroupId = null;
303                         for (Map.Entry<GroupId, Long> entry: pendingXidMaps.entrySet()) {
304                             if (entry.getValue() == errorMsg.getXid()) {
305                                 pendingGroupId = entry.getKey();
306                                 break;
307                             }
308                         }
309                         if (pendingGroupId == null) {
310                             log.warn("Error for unknown group operation: {}",
311                                     errorMsg.getXid());
312                         } else {
313                             GroupOperation operation =
314                                     pendingGroupOperations.get(pendingGroupId);
315                             DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid));
316                             if (operation != null) {
317                                 providerService.groupOperationFailed(deviceId,
318                                         operation);
319                                 pendingGroupOperations.remove(pendingGroupId);
320                                 pendingXidMaps.remove(pendingGroupId);
321                                 log.warn("Received an group mod error {}", msg);
322                             } else {
323                                 log.error("Cannot find pending group operation with group ID: {}",
324                                         pendingGroupId);
325                             }
326                         }
327                         break;
328                     }
329                 default:
330                     break;
331             }
332         }
333
334         @Override
335         public void switchAdded(Dpid dpid) {
336             OpenFlowSwitch sw = controller.getSwitch(dpid);
337             if (sw == null) {
338                 return;
339             }
340             if (isGroupSupported(sw)) {
341                 GroupStatsCollector gsc = new GroupStatsCollector(
342                         controller.getSwitch(dpid), POLL_INTERVAL);
343                 gsc.start();
344                 collectors.put(dpid, gsc);
345             }
346
347             //figure out race condition
348             if (controller.getSwitch(dpid) == null) {
349                 switchRemoved(dpid);
350             }
351         }
352
353         @Override
354         public void switchRemoved(Dpid dpid) {
355             GroupStatsCollector collector = collectors.remove(dpid);
356             if (collector != null) {
357                 collector.stop();
358             }
359         }
360
361         @Override
362         public void switchChanged(Dpid dpid) {
363         }
364
365         @Override
366         public void portChanged(Dpid dpid, OFPortStatus status) {
367         }
368
369         @Override
370         public void receivedRoleReply(Dpid dpid, RoleState requested, RoleState response) {
371         }
372     }
373
374 }