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