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 static org.slf4j.LoggerFactory.getLogger;
21 import java.util.Collection;
23 import java.util.Optional;
24 import java.util.concurrent.atomic.AtomicLong;
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;
69 import com.google.common.collect.Maps;
72 * Provider which uses an OpenFlow controller to handle Group.
74 @Component(immediate = true)
75 public class OpenFlowGroupProvider extends AbstractProvider implements GroupProvider {
77 private final Logger log = getLogger(getClass());
79 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
80 protected OpenFlowController controller;
82 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
83 protected GroupProviderRegistry providerRegistry;
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected DriverService driverService;
88 private GroupProviderService providerService;
90 static final int POLL_INTERVAL = 10;
92 private final InternalGroupProvider listener = new InternalGroupProvider();
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();
100 /* Map<Group ID, Transaction ID> */
101 private final Map<GroupId, Long> pendingXidMaps = Maps.newConcurrentMap();
104 * Creates a OpenFlow group provider.
106 public OpenFlowGroupProvider() {
107 super(new ProviderId("of", "org.onosproject.provider.group"));
111 public void activate() {
112 providerService = providerRegistry.register(this);
113 controller.addListener(listener);
114 controller.addEventListener(listener);
116 for (OpenFlowSwitch sw : controller.getSwitches()) {
117 if (isGroupSupported(sw)) {
118 GroupStatsCollector gsc = new GroupStatsCollector(sw, POLL_INTERVAL);
120 collectors.put(new Dpid(sw.getId()), gsc);
128 public void deactivate() {
129 providerRegistry.unregister(this);
130 providerService = null;
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()) {
142 log.error("SW {} is not found", dpid);
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(),
152 Optional.of(groupModXid));
154 builder = GroupModBuilder.builder(groupOperation.buckets(),
155 groupOperation.groupId(),
156 groupOperation.groupType(),
158 Optional.of(groupModXid),
159 Optional.of(driverService));
161 OFGroupMod groupMod = null;
162 switch (groupOperation.opType()) {
164 groupMod = builder.buildGroupAdd();
167 groupMod = builder.buildGroupMod();
170 groupMod = builder.buildGroupDel();
173 log.error("Unsupported Group operation");
176 sw.sendMsg(groupMod);
177 GroupId groudId = new DefaultGroupId(groupMod.getGroup().getGroupNumber());
178 pendingGroupOperations.put(groudId, groupOperation);
179 pendingXidMaps.put(groudId, groupModXid);
183 private void pushGroupMetrics(Dpid dpid, OFStatsReply statsReply) {
184 DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid));
186 OFGroupStatsReply groupStatsReply = null;
187 OFGroupDescStatsReply groupDescStatsReply = null;
189 synchronized (groupStats) {
190 if (statsReply.getStatsType() == OFStatsType.GROUP) {
191 OFStatsReply reply = groupStats.get(statsReply.getXid() + 1);
193 groupStatsReply = (OFGroupStatsReply) statsReply;
194 groupDescStatsReply = (OFGroupDescStatsReply) reply;
195 groupStats.remove(statsReply.getXid() + 1);
197 groupStats.put(statsReply.getXid(), statsReply);
199 } else if (statsReply.getStatsType() == OFStatsType.GROUP_DESC) {
200 OFStatsReply reply = groupStats.get(statsReply.getXid() - 1);
202 groupStatsReply = (OFGroupStatsReply) reply;
203 groupDescStatsReply = (OFGroupDescStatsReply) statsReply;
204 groupStats.remove(statsReply.getXid() - 1);
206 groupStats.put(statsReply.getXid(), statsReply);
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());
222 private Collection<Group> buildGroupMetrics(DeviceId deviceId,
223 OFGroupStatsReply groupStatsReply,
224 OFGroupDescStatsReply groupDescStatsReply) {
226 Map<Integer, Group> groups = Maps.newHashMap();
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);
239 for (OFGroupStatsEntry entry: groupStatsReply.getEntries()) {
240 int groupId = entry.getGroup().getGroupNumber();
241 DefaultGroup group = (DefaultGroup) groups.get(groupId);
243 group.setBytes(entry.getByteCount().getValue());
244 group.setLife(entry.getDurationSec());
245 group.setPackets(entry.getPacketCount().getValue());
246 group.setReferenceCount(entry.getRefCount());
248 for (OFBucketCounter bucketStats:entry.getBucketStats()) {
249 ((StoredGroupBucketEntry) group.buckets().buckets()
251 .setPackets(bucketStats
252 .getPacketCount().getValue());
253 ((StoredGroupBucketEntry) group.buckets().buckets()
255 .setBytes(entry.getBucketStats()
257 .getByteCount().getValue());
263 return groups.values();
266 private GroupDescription.Type getGroupType(OFGroupType type) {
269 return GroupDescription.Type.ALL;
271 return GroupDescription.Type.INDIRECT;
273 return GroupDescription.Type.SELECT;
275 return GroupDescription.Type.FAILOVER;
277 log.error("Unsupported OF group type : {}", type);
284 * Returns a transaction ID for entire group operations and increases
285 * the counter by the number given.
287 * @param increase the amount to increase the counter by
288 * @return a transaction ID
290 public static long getXidAndAdd(int increase) {
291 return XID_COUNTER.getAndAdd(increase);
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) {
304 private class InternalGroupProvider
305 implements OpenFlowSwitchListener, OpenFlowEventListener {
308 public void handleMessage(Dpid dpid, OFMessage msg) {
309 switch (msg.getType()) {
311 pushGroupMetrics(dpid, (OFStatsReply) msg);
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();
323 if (pendingGroupId == null) {
324 log.warn("Error for unknown group operation: {}",
327 GroupOperation operation =
328 pendingGroupOperations.get(pendingGroupId);
329 DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid));
330 if (operation != null) {
331 providerService.groupOperationFailed(deviceId,
333 pendingGroupOperations.remove(pendingGroupId);
334 pendingXidMaps.remove(pendingGroupId);
335 log.warn("Received an group mod error {}", msg);
337 log.error("Cannot find pending group operation with group ID: {}",
349 public void switchAdded(Dpid dpid) {
350 OpenFlowSwitch sw = controller.getSwitch(dpid);
354 if (isGroupSupported(sw)) {
355 GroupStatsCollector gsc = new GroupStatsCollector(
356 controller.getSwitch(dpid), POLL_INTERVAL);
358 collectors.put(dpid, gsc);
361 //figure out race condition
362 if (controller.getSwitch(dpid) == null) {
368 public void switchRemoved(Dpid dpid) {
369 GroupStatsCollector collector = collectors.remove(dpid);
370 if (collector != null) {
376 public void switchChanged(Dpid dpid) {
380 public void portChanged(Dpid dpid, OFPortStatus status) {
384 public void receivedRoleReply(Dpid dpid, RoleState requested, RoleState response) {