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