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.group.impl;
19 import com.google.common.collect.Maps;
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;
63 import java.util.Collection;
65 import java.util.Optional;
66 import java.util.concurrent.atomic.AtomicLong;
68 import static org.slf4j.LoggerFactory.getLogger;
71 * Provider which uses an OpenFlow controller to handle Group.
73 @Component(immediate = true)
74 public class OpenFlowGroupProvider extends AbstractProvider implements GroupProvider {
76 private final Logger log = getLogger(getClass());
78 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
79 protected OpenFlowController controller;
81 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
82 protected GroupProviderRegistry providerRegistry;
84 private GroupProviderService providerService;
86 static final int POLL_INTERVAL = 10;
88 private final InternalGroupProvider listener = new InternalGroupProvider();
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();
96 /* Map<Group ID, Transaction ID> */
97 private final Map<GroupId, Long> pendingXidMaps = Maps.newConcurrentMap();
100 * Creates a OpenFlow group provider.
102 public OpenFlowGroupProvider() {
103 super(new ProviderId("of", "org.onosproject.provider.group"));
107 public void activate() {
108 providerService = providerRegistry.register(this);
109 controller.addListener(listener);
110 controller.addEventListener(listener);
112 for (OpenFlowSwitch sw : controller.getSwitches()) {
113 if (isGroupSupported(sw)) {
114 GroupStatsCollector gsc = new GroupStatsCollector(sw, POLL_INTERVAL);
116 collectors.put(new Dpid(sw.getId()), gsc);
124 public void deactivate() {
125 providerRegistry.unregister(this);
126 providerService = null;
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()) {
138 log.error("SW {} is not found", dpid);
141 final Long groupModXid = XID_COUNTER.getAndIncrement();
142 GroupModBuilder builder =
143 GroupModBuilder.builder(groupOperation.buckets(),
144 groupOperation.groupId(),
145 groupOperation.groupType(),
147 Optional.of(groupModXid));
148 OFGroupMod groupMod = null;
149 switch (groupOperation.opType()) {
151 groupMod = builder.buildGroupAdd();
154 groupMod = builder.buildGroupMod();
157 groupMod = builder.buildGroupDel();
160 log.error("Unsupported Group operation");
162 sw.sendMsg(groupMod);
163 GroupId groudId = new DefaultGroupId(groupMod.getGroup().getGroupNumber());
164 pendingGroupOperations.put(groudId, groupOperation);
165 pendingXidMaps.put(groudId, groupModXid);
169 private void pushGroupMetrics(Dpid dpid, OFStatsReply statsReply) {
170 DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid));
172 OFGroupStatsReply groupStatsReply = null;
173 OFGroupDescStatsReply groupDescStatsReply = null;
175 synchronized (groupStats) {
176 if (statsReply.getStatsType() == OFStatsType.GROUP) {
177 OFStatsReply reply = groupStats.get(statsReply.getXid() + 1);
179 groupStatsReply = (OFGroupStatsReply) statsReply;
180 groupDescStatsReply = (OFGroupDescStatsReply) reply;
181 groupStats.remove(statsReply.getXid() + 1);
183 groupStats.put(statsReply.getXid(), statsReply);
185 } else if (statsReply.getStatsType() == OFStatsType.GROUP_DESC) {
186 OFStatsReply reply = groupStats.get(statsReply.getXid() - 1);
188 groupStatsReply = (OFGroupStatsReply) reply;
189 groupDescStatsReply = (OFGroupDescStatsReply) statsReply;
190 groupStats.remove(statsReply.getXid() - 1);
192 groupStats.put(statsReply.getXid(), statsReply);
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());
208 private Collection<Group> buildGroupMetrics(DeviceId deviceId,
209 OFGroupStatsReply groupStatsReply,
210 OFGroupDescStatsReply groupDescStatsReply) {
212 Map<Integer, Group> groups = Maps.newHashMap();
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);
225 for (OFGroupStatsEntry entry: groupStatsReply.getEntries()) {
226 int groupId = entry.getGroup().getGroupNumber();
227 DefaultGroup group = (DefaultGroup) groups.get(groupId);
229 group.setBytes(entry.getByteCount().getValue());
230 group.setLife(entry.getDurationSec());
231 group.setPackets(entry.getPacketCount().getValue());
232 group.setReferenceCount(entry.getRefCount());
234 for (OFBucketCounter bucketStats:entry.getBucketStats()) {
235 ((StoredGroupBucketEntry) group.buckets().buckets()
237 .setPackets(bucketStats
238 .getPacketCount().getValue());
239 ((StoredGroupBucketEntry) group.buckets().buckets()
241 .setBytes(entry.getBucketStats()
243 .getByteCount().getValue());
249 return groups.values();
252 private GroupDescription.Type getGroupType(OFGroupType type) {
255 return GroupDescription.Type.ALL;
257 return GroupDescription.Type.INDIRECT;
259 return GroupDescription.Type.SELECT;
261 return GroupDescription.Type.FAILOVER;
263 log.error("Unsupported OF group type : {}", type);
270 * Returns a transaction ID for entire group operations and increases
271 * the counter by the number given.
273 * @param increase the amount to increase the counter by
274 * @return a transaction ID
276 public static long getXidAndAdd(int increase) {
277 return XID_COUNTER.getAndAdd(increase);
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) {
290 private class InternalGroupProvider
291 implements OpenFlowSwitchListener, OpenFlowEventListener {
294 public void handleMessage(Dpid dpid, OFMessage msg) {
295 switch (msg.getType()) {
297 pushGroupMetrics(dpid, (OFStatsReply) msg);
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();
309 if (pendingGroupId == null) {
310 log.warn("Error for unknown group operation: {}",
313 GroupOperation operation =
314 pendingGroupOperations.get(pendingGroupId);
315 DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid));
316 if (operation != null) {
317 providerService.groupOperationFailed(deviceId,
319 pendingGroupOperations.remove(pendingGroupId);
320 pendingXidMaps.remove(pendingGroupId);
321 log.warn("Received an group mod error {}", msg);
323 log.error("Cannot find pending group operation with group ID: {}",
335 public void switchAdded(Dpid dpid) {
336 OpenFlowSwitch sw = controller.getSwitch(dpid);
340 if (isGroupSupported(sw)) {
341 GroupStatsCollector gsc = new GroupStatsCollector(
342 controller.getSwitch(dpid), POLL_INTERVAL);
344 collectors.put(dpid, gsc);
347 //figure out race condition
348 if (controller.getSwitch(dpid) == null) {
354 public void switchRemoved(Dpid dpid) {
355 GroupStatsCollector collector = collectors.remove(dpid);
356 if (collector != null) {
362 public void switchChanged(Dpid dpid) {
366 public void portChanged(Dpid dpid, OFPortStatus status) {
370 public void receivedRoleReply(Dpid dpid, RoleState requested, RoleState response) {