blob: 1c9f80123a1a07c8ae72e23d0e638da8d7921485 [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;
57import org.slf4j.Logger;
58
59import java.util.Collection;
60import java.util.Map;
61import java.util.Optional;
62import java.util.concurrent.atomic.AtomicLong;
63
64import static org.slf4j.LoggerFactory.getLogger;
65
66/**
67 * Provider which uses an OpenFlow controller to handle Group.
68 */
69@Component(immediate = true)
70public class OpenFlowGroupProvider extends AbstractProvider implements GroupProvider {
71
72 private final Logger log = getLogger(getClass());
73
74 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
75 protected OpenFlowController controller;
76
77 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
78 protected GroupProviderRegistry providerRegistry;
79
80 private GroupProviderService providerService;
81
82 static final int POLL_INTERVAL = 10;
83
84 private final InternalGroupProvider listener = new InternalGroupProvider();
85
sanghoa09f2742015-02-06 14:49:47 -080086 private static final AtomicLong XID_COUNTER = new AtomicLong(1);
sangho5afd02a2015-02-03 20:07:35 -080087 private final Map<Dpid, GroupStatsCollector> collectors = Maps.newHashMap();
sanghoa09f2742015-02-06 14:49:47 -080088 private final Map<Long, OFStatsReply> groupStats = Maps.newConcurrentMap();
89 private final Map<Integer, GroupOperation> pendingGroupOperations =
sangho5afd02a2015-02-03 20:07:35 -080090 Maps.newConcurrentMap();
91
sanghoa09f2742015-02-06 14:49:47 -080092 /* Map<Group ID, Transaction ID> */
93 private final Map<Integer, Long> pendingXidMaps = Maps.newConcurrentMap();
94
sangho5afd02a2015-02-03 20:07:35 -080095 /**
96 * Creates a OpenFlow group provider.
97 */
98 public OpenFlowGroupProvider() {
99 super(new ProviderId("of", "org.onosproject.provider.group"));
100 }
101
102 @Activate
103 public void activate() {
104 providerService = providerRegistry.register(this);
105 controller.addListener(listener);
106 controller.addEventListener(listener);
107
108 for (OpenFlowSwitch sw : controller.getSwitches()) {
109 GroupStatsCollector gsc = new GroupStatsCollector(sw, POLL_INTERVAL);
110 gsc.start();
111 collectors.put(new Dpid(sw.getId()), gsc);
112 }
113
114 log.info("Started");
115 }
116
117 @Deactivate
118 public void deactivate() {
119 providerRegistry.unregister(this);
120 providerService = null;
121
122 log.info("Stopped");
123 }
124
125 @Override
126 public void performGroupOperation(DeviceId deviceId, GroupOperations groupOps) {
127 Map<OFGroupMod, OpenFlowSwitch> mods = Maps.newIdentityHashMap();
128 final Dpid dpid = Dpid.dpid(deviceId.uri());
129 OpenFlowSwitch sw = controller.getSwitch(dpid);
130 for (GroupOperation groupOperation: groupOps.operations()) {
131 if (sw == null) {
sanghoa09f2742015-02-06 14:49:47 -0800132 log.error("SW {} is not found", dpid);
sangho5afd02a2015-02-03 20:07:35 -0800133 return;
134 }
sanghoa09f2742015-02-06 14:49:47 -0800135 final Long groupModXid = XID_COUNTER.getAndIncrement();
sangho5afd02a2015-02-03 20:07:35 -0800136 GroupModBuilder builder =
137 GroupModBuilder.builder(groupOperation.buckets(),
138 groupOperation.groupId(),
139 groupOperation.groupType(),
140 sw.factory(),
141 Optional.of(groupModXid));
142 OFGroupMod groupMod = null;
143 switch (groupOperation.opType()) {
144 case ADD:
145 groupMod = builder.buildGroupAdd();
146 break;
147 case MODIFY:
148 groupMod = builder.buildGroupMod();
149 break;
150 case DELETE:
151 groupMod = builder.buildGroupDel();
152 break;
153 default:
154 log.error("Unsupported Group operation");
155 }
156 sw.sendMsg(groupMod);
sanghoa09f2742015-02-06 14:49:47 -0800157 pendingGroupOperations.put(groupMod.getGroup().getGroupNumber(),
158 groupOperation);
159 pendingXidMaps.put(groupMod.getGroup().getGroupNumber(), groupModXid);
sangho5afd02a2015-02-03 20:07:35 -0800160 }
161 }
162
163 private void pushGroupMetrics(Dpid dpid, OFStatsReply statsReply) {
164 DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid));
165
166 OFGroupStatsReply groupStatsReply = null;
167 OFGroupDescStatsReply groupDescStatsReply = null;
168
sanghoa09f2742015-02-06 14:49:47 -0800169 synchronized (groupStats) {
170 if (statsReply.getStatsType() == OFStatsType.GROUP) {
171 OFStatsReply reply = groupStats.get(statsReply.getXid() + 1);
172 if (reply != null) {
173 groupStatsReply = (OFGroupStatsReply) statsReply;
174 groupDescStatsReply = (OFGroupDescStatsReply) reply;
175 groupStats.remove(statsReply.getXid() + 1);
176 } else {
177 groupStats.put(statsReply.getXid(), statsReply);
178 }
179 } else if (statsReply.getStatsType() == OFStatsType.GROUP_DESC) {
180 OFStatsReply reply = groupStats.get(statsReply.getXid() - 1);
181 if (reply != null) {
182 groupStatsReply = (OFGroupStatsReply) reply;
183 groupDescStatsReply = (OFGroupDescStatsReply) statsReply;
184 groupStats.remove(statsReply.getXid() - 1);
185 } else {
186 groupStats.put(statsReply.getXid(), statsReply);
187 }
sangho5afd02a2015-02-03 20:07:35 -0800188 }
189 }
190
191 if (groupStatsReply != null && groupDescStatsReply != null) {
192 Collection<Group> groups = buildGroupMetrics(deviceId,
193 groupStatsReply, groupDescStatsReply);
194 providerService.pushGroupMetrics(deviceId, groups);
195 for (Group group: groups) {
196 pendingGroupOperations.remove(group.id());
sanghoa09f2742015-02-06 14:49:47 -0800197 pendingXidMaps.remove(group.id());
sangho5afd02a2015-02-03 20:07:35 -0800198 }
199 }
200 }
201
202 private Collection<Group> buildGroupMetrics(DeviceId deviceId,
203 OFGroupStatsReply groupStatsReply,
204 OFGroupDescStatsReply groupDescStatsReply) {
205
206 Map<Integer, Group> groups = Maps.newHashMap();
207
208 for (OFGroupDescStatsEntry entry: groupDescStatsReply.getEntries()) {
209 int id = entry.getGroup().getGroupNumber();
210 GroupId groupId = new DefaultGroupId(id);
211 GroupDescription.Type type = getGroupType(entry.getGroupType());
212 GroupBuckets buckets = new GroupBucketEntryBuilder(entry.getBuckets(),
213 entry.getGroupType()).build();
214 DefaultGroup group = new DefaultGroup(groupId, deviceId, type, buckets);
215 groups.put(id, group);
216 }
217
218 for (OFGroupStatsEntry entry: groupStatsReply.getEntries()) {
219 int groupId = entry.getGroup().getGroupNumber();
220 DefaultGroup group = (DefaultGroup) groups.get(groupId);
221 if (group != null) {
222 group.setBytes(entry.getByteCount().getValue());
223 group.setLife(entry.getDurationSec());
224 group.setPackets(entry.getPacketCount().getValue());
225 group.setReferenceCount(entry.getRefCount());
226 }
227 }
228
229 return groups.values();
230 }
231
232 private GroupDescription.Type getGroupType(OFGroupType type) {
233 switch (type) {
234 case ALL:
235 return GroupDescription.Type.ALL;
236 case INDIRECT:
237 return GroupDescription.Type.INDIRECT;
238 case SELECT:
239 return GroupDescription.Type.SELECT;
240 case FF:
241 return GroupDescription.Type.FAILOVER;
242 default:
243 log.error("Unsupported OF group type : {}", type);
244 break;
245 }
246 return null;
247 }
248
sanghoa09f2742015-02-06 14:49:47 -0800249 /**
250 * Returns a transaction ID for entire group operations and increases
251 * the counter by the number given.
252 *
253 * @param increase the amount to increase the counter by
254 * @return a transaction ID
255 */
256 public static long getXidAndAdd(int increase) {
257 return XID_COUNTER.getAndAdd(increase);
258 }
259
sangho5afd02a2015-02-03 20:07:35 -0800260 private class InternalGroupProvider
261 implements OpenFlowSwitchListener, OpenFlowEventListener {
262
263 @Override
264 public void handleMessage(Dpid dpid, OFMessage msg) {
265 switch (msg.getType()) {
266 case STATS_REPLY:
267 pushGroupMetrics(dpid, (OFStatsReply) msg);
268 break;
269 case ERROR:
270 OFErrorMsg errorMsg = (OFErrorMsg) msg;
271 if (errorMsg.getErrType() == OFErrorType.GROUP_MOD_FAILED) {
sanghoa09f2742015-02-06 14:49:47 -0800272 int pendingGroupId = -1;
273 for (Map.Entry<Integer, Long> entry: pendingXidMaps.entrySet()) {
274 if (entry.getValue() == errorMsg.getXid()) {
275 pendingGroupId = entry.getKey();
276 break;
277 }
278 }
279 if (pendingGroupId == -1) {
280 log.warn("Error for unknown group operation: {}",
281 errorMsg.getXid());
282 } else {
283 GroupOperation operation =
284 pendingGroupOperations.get(pendingGroupId);
285 if (operation != null) {
286 providerService.groupOperationFailed(operation);
287 pendingGroupOperations.remove(pendingGroupId);
288 pendingXidMaps.remove(pendingGroupId);
289 log.warn("Received an group mod error {}", msg);
290 } else {
291 log.error("Cannot find pending group operation with group ID: {}",
292 pendingGroupId);
293 }
sangho5afd02a2015-02-03 20:07:35 -0800294 }
295 break;
296 }
297 default:
298 log.debug("Unhandled message type: {}", msg.getType());
299 }
300 }
301
302 @Override
303 public void switchAdded(Dpid dpid) {
304 GroupStatsCollector gsc = new GroupStatsCollector(
305 controller.getSwitch(dpid), POLL_INTERVAL);
306 gsc.start();
307 collectors.put(dpid, gsc);
308 }
309
310 @Override
311 public void switchRemoved(Dpid dpid) {
312 GroupStatsCollector collector = collectors.remove(dpid);
313 if (collector != null) {
314 collector.stop();
315 }
316 }
317
318 @Override
319 public void switchChanged(Dpid dpid) {
320 }
321
322 @Override
323 public void portChanged(Dpid dpid, OFPortStatus status) {
324 }
325
326 @Override
327 public void receivedRoleReply(Dpid dpid, RoleState requested, RoleState response) {
328 }
329 }
330
331}