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