blob: 755fac234efd45399b6a6ec24c67ac8cb39a0524 [file] [log] [blame]
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -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 */
16package org.onosproject.grouphandler;
17
18import static com.google.common.base.Preconditions.checkNotNull;
19import static org.slf4j.LoggerFactory.getLogger;
20
21import java.util.ArrayList;
22import java.util.Arrays;
23import java.util.HashMap;
24import java.util.HashSet;
25import java.util.List;
26import java.util.Set;
27
28import org.onlab.packet.MacAddress;
sangho32a59322015-02-17 12:07:41 -080029import org.onlab.packet.MplsLabel;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080030import org.onosproject.core.ApplicationId;
31import org.onosproject.net.DeviceId;
32import org.onosproject.net.Link;
33import org.onosproject.net.PortNumber;
34import org.onosproject.net.flow.DefaultTrafficTreatment;
35import org.onosproject.net.flow.TrafficTreatment;
36import org.onosproject.net.group.DefaultGroupBucket;
37import org.onosproject.net.group.DefaultGroupDescription;
38import org.onosproject.net.group.Group;
39import org.onosproject.net.group.GroupBucket;
40import org.onosproject.net.group.GroupBuckets;
41import org.onosproject.net.group.GroupDescription;
42import org.onosproject.net.group.GroupEvent;
43import org.onosproject.net.group.GroupKey;
44import org.onosproject.net.group.GroupListener;
45import org.onosproject.net.group.GroupService;
46import org.onosproject.net.link.LinkService;
47import org.slf4j.Logger;
48
49/**
50 * Default ECMP group handler creation module. This
51 * component creates a set of ECMP groups for every neighbor
52 * that this device is connected to based on whether the
53 * current device is an edge device or a transit device.
54 */
55public class DefaultGroupHandler {
56 protected final Logger log = getLogger(getClass());
57
58 protected final DeviceId deviceId;
59 protected final ApplicationId appId;
60 protected final DeviceProperties deviceConfig;
61 protected final List<Integer> allSegmentIds;
62 protected final int nodeSegmentId;
63 protected final boolean isEdgeRouter;
64 protected final MacAddress nodeMacAddr;
65 protected LinkService linkService;
66 protected GroupService groupService;
67
68 protected HashMap<DeviceId, Set<PortNumber>> devicePortMap =
69 new HashMap<DeviceId, Set<PortNumber>>();
70 protected HashMap<PortNumber, DeviceId> portDeviceMap =
71 new HashMap<PortNumber, DeviceId>();
72
73 private GroupListener listener = new InternalGroupListener();
74
75 protected DefaultGroupHandler(DeviceId deviceId,
76 ApplicationId appId,
77 DeviceProperties config,
78 LinkService linkService,
79 GroupService groupService) {
80 this.deviceId = checkNotNull(deviceId);
81 this.appId = checkNotNull(appId);
82 this.deviceConfig = checkNotNull(config);
83 this.linkService = checkNotNull(linkService);
84 this.groupService = checkNotNull(groupService);
85 allSegmentIds = checkNotNull(config.getAllDeviceSegmentIds());
86 nodeSegmentId = config.getSegmentId(deviceId);
87 isEdgeRouter = config.isEdgeDevice(deviceId);
88 nodeMacAddr = checkNotNull(config.getDeviceMac(deviceId));
89
90 this.groupService.addListener(listener);
91
92 populateNeighborMaps();
93 }
94
95 /**
96 * Creates a group handler object based on the type of device. If
97 * device is of edge type it returns edge group handler, else it
98 * returns transit group handler.
99 *
100 * @param deviceId device identifier
101 * @param appId application identifier
102 * @param config interface to retrieve the device properties
103 * @param linkService link service object
104 * @param groupService group service object
105 * @return default group handler type
106 */
107 public static DefaultGroupHandler createGroupHandler(DeviceId deviceId,
108 ApplicationId appId,
109 DeviceProperties config,
110 LinkService linkService,
111 GroupService groupService) {
112 if (config.isEdgeDevice(deviceId)) {
113 return new DefaultEdgeGroupHandler(deviceId,
114 appId,
115 config,
116 linkService,
117 groupService);
118 } else {
119 return new DefaultTransitGroupHandler(deviceId,
120 appId,
121 config,
122 linkService,
123 groupService);
124 }
125 }
126
127 /**
128 * Creates the auto created groups for this device based on the
129 * current snapshot of the topology.
130 */
131 //Empty implementations to be overridden by derived classes
132 public void createGroups() {
133 }
134
135 /**
136 * Performs group creation or update procedures when a new link
137 * is discovered on this device.
138 *
139 * @param newLink new neighbor link
140 */
141 public void linkUp(Link newLink) {
142 if (newLink.type() != Link.Type.DIRECT) {
143 log.warn("linkUp: unknown link type");
144 return;
145 }
146
147 if (!newLink.src().deviceId().equals(deviceId)) {
148 log.warn("linkUp: deviceId{} doesn't match with link src{}",
149 deviceId,
150 newLink.src().deviceId());
151 return;
152 }
153
154 log.debug("Device {} linkUp at local port {} to neighbor {}",
155 deviceId, newLink.src().port(), newLink.dst().deviceId());
156 if (devicePortMap.get(newLink.dst().deviceId()) == null) {
157 // New Neighbor
158 newNeighbor(newLink);
159 } else {
160 // Old Neighbor
161 newPortToExistingNeighbor(newLink);
162 }
163 }
164
165 /**
166 * Performs group recovery procedures when a port goes down
167 * on this device.
168 *
169 * @param port port number that has gone down
170 */
171 public void portDown(PortNumber port) {
172 if (portDeviceMap.get(port) == null) {
173 log.warn("portDown: unknown port");
174 return;
175 }
176 log.debug("Device {} portDown {} to neighbor {}",
177 deviceId, port, portDeviceMap.get(port));
178 Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(
179 portDeviceMap.get(port),
180 devicePortMap.keySet());
181 for (NeighborSet ns : nsSet) {
182 // Create the bucket to be removed
183 TrafficTreatment.Builder tBuilder =
184 DefaultTrafficTreatment.builder();
185 tBuilder.setOutput(port)
186 .setEthDst(deviceConfig.getDeviceMac(
187 portDeviceMap.get(port)))
188 .setEthSrc(nodeMacAddr)
189 .pushMpls()
sangho32a59322015-02-17 12:07:41 -0800190 .setMpls(MplsLabel.mplsLabel(ns.getEdgeLabel()));
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800191 GroupBucket removeBucket = DefaultGroupBucket.
192 createSelectGroupBucket(tBuilder.build());
193 GroupBuckets removeBuckets = new GroupBuckets(
194 Arrays.asList(removeBucket));
195 log.debug("portDown in device{}: "
196 + "groupService.removeBucketsFromGroup "
197 + "for neighborset{}", deviceId, ns);
198 groupService.removeBucketsFromGroup(deviceId,
199 ns,
200 removeBuckets,
201 ns,
202 appId);
203 }
204
205 devicePortMap.get(portDeviceMap.get(port)).remove(port);
206 portDeviceMap.remove(port);
207 }
208
209 /**
210 * Returns a group associated with the key.
211 *
212 * @param key cookie associated with the group
213 * @return group if found or null
214 */
215 public Group getGroup(GroupKey key) {
216 return groupService.getGroup(deviceId, key);
217 }
218
219 //Empty implementation
220 protected void newNeighbor(Link newLink) {
221 }
222
223 //Empty implementation
224 protected void newPortToExistingNeighbor(Link newLink) {
225 }
226
227 //Empty implementation
228 protected Set<NeighborSet> computeImpactedNeighborsetForPortEvent(
229 DeviceId impactedNeighbor,
230 Set<DeviceId> updatedNeighbors) {
231 return null;
232 }
233
234 private void populateNeighborMaps() {
235 Set<Link> outgoingLinks = linkService.getDeviceEgressLinks(deviceId);
236 for (Link link:outgoingLinks) {
237 if (link.type() != Link.Type.DIRECT) {
238 continue;
239 }
240 addNeighborAtPort(link.dst().deviceId(), link.src().port());
241 }
242 }
243
244 protected void addNeighborAtPort(DeviceId neighborId, PortNumber portToNeighbor) {
245 // Update DeviceToPort database
246 log.debug("Device {} addNeighborAtPort: neighbor {} at port {}",
247 deviceId, neighborId, portToNeighbor);
248 if (devicePortMap.get(neighborId) != null) {
249 devicePortMap.get(neighborId).add(portToNeighbor);
250 } else {
251 Set<PortNumber> ports = new HashSet<PortNumber>();
252 ports.add(portToNeighbor);
253 devicePortMap.put(neighborId, ports);
254 }
255
256 // Update portToDevice database
257 if (portDeviceMap.get(portToNeighbor) == null) {
258 portDeviceMap.put(portToNeighbor, neighborId);
259 }
260 }
261
262 protected Set<Set<DeviceId>>
263 getPowerSetOfNeighbors(Set<DeviceId> neighbors) {
264 List<DeviceId> list = new ArrayList<DeviceId>(neighbors);
265 Set<Set<DeviceId>> sets = new HashSet<Set<DeviceId>>();
266 // get the number of elements in the neighbors
267 int elements = list.size();
268 // the number of members of a power set is 2^n
269 // including the empty set
270 int powerElements = (1 << elements);
271
272 // run a binary counter for the number of power elements
273 // NOTE: Exclude empty set
274 for (long i = 1; i < powerElements; i++) {
275 Set<DeviceId> neighborSubSet = new HashSet<DeviceId>();
276 for (int j = 0; j < elements; j++) {
277 if ((i >> j) % 2 == 1) {
278 neighborSubSet.add(list.get(j));
279 }
280 }
281 sets.add(neighborSubSet);
282 }
283 return sets;
284 }
285
286 private boolean isSegmentIdSameAsNodeSegmentId(DeviceId deviceId, int sId) {
287 return (deviceConfig.getSegmentId(deviceId) == sId);
288 }
289
290 protected List<Integer> getSegmentIdsTobePairedWithNeighborSet(
291 Set<DeviceId> neighbors) {
292
293 List<Integer> nsSegmentIds = new ArrayList<Integer>();
294
295 // Add one entry for "no label" (-1) to the list if
296 // dpid list has not more than one node/neighbor as
297 // there will never be a case a packet going to more than one
298 // neighbor without a label at an edge router
299 if (neighbors.size() == 1) {
300 nsSegmentIds.add(-1);
301 }
302 // Filter out SegmentIds matching with the
303 // nodes in the combo
304 for (Integer sId : allSegmentIds) {
305 if (sId.equals(nodeSegmentId)) {
306 continue;
307 }
308 boolean filterOut = false;
309 // Check if the edge label being set is of
310 // any node in the Neighbor set
311 for (DeviceId deviceId : neighbors) {
312 if (isSegmentIdSameAsNodeSegmentId(deviceId, sId)) {
313 filterOut = true;
314 break;
315 }
316 }
317 if (!filterOut) {
318 nsSegmentIds.add(sId);
319 }
320 }
321 return nsSegmentIds;
322 }
323
324 protected void createGroupsFromNeighborsets(Set<NeighborSet> nsSet) {
325 for (NeighborSet ns : nsSet) {
326 // Create the bucket array from the neighbor set
327 List<GroupBucket> buckets = new ArrayList<GroupBucket>();
328 for (DeviceId d : ns.getDeviceIds()) {
329 for (PortNumber sp : devicePortMap.get(d)) {
330 TrafficTreatment.Builder tBuilder =
331 DefaultTrafficTreatment.builder();
332 tBuilder.setOutput(sp)
333 .setEthDst(deviceConfig.getDeviceMac(d))
334 .setEthSrc(nodeMacAddr)
335 .pushMpls()
sangho32a59322015-02-17 12:07:41 -0800336 .setMpls(MplsLabel.mplsLabel(ns.getEdgeLabel()));
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800337 buckets.add(DefaultGroupBucket.createSelectGroupBucket(
338 tBuilder.build()));
339 }
340 }
341 GroupBuckets groupBuckets = new GroupBuckets(buckets);
342 GroupDescription newGroupDesc = new DefaultGroupDescription(
343 deviceId,
344 Group.Type.SELECT,
345 groupBuckets,
346 ns,
347 appId);
348 log.debug("createGroupsFromNeighborsets: "
349 + "groupService.addGroup for neighborset{}", ns);
350 groupService.addGroup(newGroupDesc);
351 }
352 }
353
354 protected void handleGroupEvent(GroupEvent event) {
355 switch (event.type()) {
356 case GROUP_ADDED:
357 log.debug("Received GROUP_ADDED from group service "
358 + "for device {} with group key{} with id{}",
359 event.subject().deviceId(),
360 event.subject().appCookie(),
361 event.subject().id());
362 break;
363 case GROUP_UPDATED:
364 log.trace("Received GROUP_UPDATED from group service "
365 + "for device {} with group key{} with id{}",
366 event.subject().deviceId(),
367 event.subject().appCookie(),
368 event.subject().id());
369 break;
370 case GROUP_REMOVED:
371 log.debug("Received GROUP_REMOVED from group service "
372 + "for device {} with group key{} with id{}",
373 event.subject().deviceId(),
374 event.subject().appCookie(),
375 event.subject().id());
376 break;
377 default:
378 break;
379 }
380 }
381
382 private class InternalGroupListener implements GroupListener {
383
384 @Override
385 public void event(GroupEvent event) {
386 handleGroupEvent(event);
387 }
388 }
389}