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