blob: cb295db6fcb4d17e4100a23fbd55827d33c52ff6 [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 */
Srikanth Vavilapalli4db76e32015-04-07 15:12:32 -070016package org.onosproject.segmentrouting.grouphandler;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080017
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;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070023import java.util.Collections;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080024import java.util.HashMap;
25import java.util.HashSet;
26import java.util.List;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070027import java.util.Random;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080028import java.util.Set;
29
30import org.onlab.packet.MacAddress;
sangho32a59322015-02-17 12:07:41 -080031import org.onlab.packet.MplsLabel;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070032import org.onlab.util.KryoNamespace;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080033import org.onosproject.core.ApplicationId;
34import org.onosproject.net.DeviceId;
35import org.onosproject.net.Link;
36import org.onosproject.net.PortNumber;
37import org.onosproject.net.flow.DefaultTrafficTreatment;
38import org.onosproject.net.flow.TrafficTreatment;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070039import org.onosproject.net.flowobjective.DefaultNextObjective;
40import org.onosproject.net.flowobjective.FlowObjectiveService;
41import org.onosproject.net.flowobjective.NextObjective;
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070042import org.onosproject.net.group.DefaultGroupKey;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080043import org.onosproject.net.group.GroupKey;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080044import org.onosproject.net.link.LinkService;
45import org.slf4j.Logger;
46
47/**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070048 * Default ECMP group handler creation module. This component creates a set of
49 * ECMP groups for every neighbor that this device is connected to based on
50 * whether the current device is an edge device or a transit device.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080051 */
52public class DefaultGroupHandler {
53 protected final Logger log = getLogger(getClass());
54
55 protected final DeviceId deviceId;
56 protected final ApplicationId appId;
57 protected final DeviceProperties deviceConfig;
58 protected final List<Integer> allSegmentIds;
59 protected final int nodeSegmentId;
60 protected final boolean isEdgeRouter;
61 protected final MacAddress nodeMacAddr;
62 protected LinkService linkService;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070063 protected FlowObjectiveService flowObjectiveService;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080064
65 protected HashMap<DeviceId, Set<PortNumber>> devicePortMap =
66 new HashMap<DeviceId, Set<PortNumber>>();
67 protected HashMap<PortNumber, DeviceId> portDeviceMap =
68 new HashMap<PortNumber, DeviceId>();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070069 protected HashMap<GroupKey, Integer> deviceNextObjectiveIds =
70 new HashMap<GroupKey, Integer>();
71 protected Random rand = new Random();
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080072
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -070073 protected KryoNamespace.Builder kryo = new KryoNamespace.Builder()
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070074 .register(URI.class).register(HashSet.class)
75 .register(DeviceId.class).register(PortNumber.class)
76 .register(NeighborSet.class).register(PolicyGroupIdentifier.class)
77 .register(PolicyGroupParams.class)
78 .register(GroupBucketIdentifier.class)
79 .register(GroupBucketIdentifier.BucketOutputType.class);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080080
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070081 protected DefaultGroupHandler(DeviceId deviceId, ApplicationId appId,
82 DeviceProperties config,
83 LinkService linkService,
84 FlowObjectiveService flowObjService) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080085 this.deviceId = checkNotNull(deviceId);
86 this.appId = checkNotNull(appId);
87 this.deviceConfig = checkNotNull(config);
88 this.linkService = checkNotNull(linkService);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080089 allSegmentIds = checkNotNull(config.getAllDeviceSegmentIds());
90 nodeSegmentId = config.getSegmentId(deviceId);
91 isEdgeRouter = config.isEdgeDevice(deviceId);
92 nodeMacAddr = checkNotNull(config.getDeviceMac(deviceId));
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070093 this.flowObjectiveService = flowObjService;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080094
95 populateNeighborMaps();
96 }
97
98 /**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070099 * Creates a group handler object based on the type of device. If device is
100 * of edge type it returns edge group handler, else it returns transit group
101 * handler.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800102 *
103 * @param deviceId device identifier
104 * @param appId application identifier
105 * @param config interface to retrieve the device properties
106 * @param linkService link service object
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700107 * @param flowObjService flow objective service object
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800108 * @return default group handler type
109 */
110 public static DefaultGroupHandler createGroupHandler(DeviceId deviceId,
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700111 ApplicationId appId,
112 DeviceProperties config,
113 LinkService linkService,
114 FlowObjectiveService flowObjService) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800115 if (config.isEdgeDevice(deviceId)) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700116 return new DefaultEdgeGroupHandler(deviceId, appId, config,
117 linkService, flowObjService);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800118 } else {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700119 return new DefaultTransitGroupHandler(deviceId, appId, config,
120 linkService, flowObjService);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800121 }
122 }
123
124 /**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700125 * Creates the auto created groups for this device based on the current
126 * snapshot of the topology.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800127 */
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700128 // Empty implementations to be overridden by derived classes
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800129 public void createGroups() {
130 }
131
132 /**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700133 * Performs group creation or update procedures when a new link is
134 * discovered on this device.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800135 *
136 * @param newLink new neighbor link
137 */
138 public void linkUp(Link newLink) {
sanghob35a6192015-04-01 13:05:26 -0700139
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800140 if (newLink.type() != Link.Type.DIRECT) {
141 log.warn("linkUp: unknown link type");
142 return;
143 }
144
145 if (!newLink.src().deviceId().equals(deviceId)) {
146 log.warn("linkUp: deviceId{} doesn't match with link src{}",
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700147 deviceId, newLink.src().deviceId());
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800148 return;
149 }
150
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700151 log.debug("Device {} linkUp at local port {} to neighbor {}", deviceId,
152 newLink.src().port(), newLink.dst().deviceId());
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800153 if (devicePortMap.get(newLink.dst().deviceId()) == null) {
154 // New Neighbor
155 newNeighbor(newLink);
156 } else {
157 // Old Neighbor
158 newPortToExistingNeighbor(newLink);
159 }
160 }
161
162 /**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700163 * Performs group recovery procedures when a port goes down on this device.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800164 *
165 * @param port port number that has gone down
166 */
167 public void portDown(PortNumber port) {
168 if (portDeviceMap.get(port) == null) {
169 log.warn("portDown: unknown port");
170 return;
171 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700172 log.debug("Device {} portDown {} to neighbor {}", deviceId, port,
173 portDeviceMap.get(port));
174 Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(portDeviceMap
175 .get(port),
176 devicePortMap
177 .keySet());
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800178 for (NeighborSet ns : nsSet) {
179 // Create the bucket to be removed
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700180 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
181 .builder();
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800182 tBuilder.setOutput(port)
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700183 .setEthDst(deviceConfig.getDeviceMac(portDeviceMap
184 .get(port))).setEthSrc(nodeMacAddr);
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700185 if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700186 tBuilder.pushMpls().setMpls(MplsLabel.mplsLabel(ns
187 .getEdgeLabel()));
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700188 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700189 /*
190 * GroupBucket removeBucket = DefaultGroupBucket.
191 * createSelectGroupBucket(tBuilder.build()); GroupBuckets
192 * removeBuckets = new GroupBuckets( Arrays.asList(removeBucket));
193 * log.debug("portDown in device{}: " +
194 * "groupService.removeBucketsFromGroup " + "for neighborset{}",
195 * deviceId, ns); groupService.removeBucketsFromGroup(deviceId,
196 * getGroupKey(ns), removeBuckets, getGroupKey(ns), appId);
197 */
198 //TODO: Use next objective API to update the previously created
199 //next objectives.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800200 }
201
202 devicePortMap.get(portDeviceMap.get(port)).remove(port);
203 portDeviceMap.remove(port);
204 }
205
206 /**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700207 * Returns the next objective associated with the neighborset.
208 * If there is no next objective for this neighborset, this API
209 * would create a next objective and return.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800210 *
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700211 * @param ns neighborset
212 * @return int if found or -1
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800213 */
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700214 public int getNextObjectiveId(NeighborSet ns) {
215 Integer nextId = deviceNextObjectiveIds.get(getGroupKey(ns));
216 if (nextId == null) {
217 createGroupsFromNeighborsets(Collections.singleton(ns));
218 nextId = deviceNextObjectiveIds.get(getGroupKey(ns));
219 if (nextId == null) {
220 log.warn("getNextObjectiveId: unable to create next objective");
221 return -1;
222 }
223 }
224 return nextId.intValue();
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800225 }
226
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700227 // Empty implementation
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800228 protected void newNeighbor(Link newLink) {
229 }
230
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700231 // Empty implementation
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800232 protected void newPortToExistingNeighbor(Link newLink) {
233 }
234
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700235 // Empty implementation
236 protected Set<NeighborSet>
237 computeImpactedNeighborsetForPortEvent(DeviceId impactedNeighbor,
238 Set<DeviceId> updatedNeighbors) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800239 return null;
240 }
241
242 private void populateNeighborMaps() {
243 Set<Link> outgoingLinks = linkService.getDeviceEgressLinks(deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700244 for (Link link : outgoingLinks) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800245 if (link.type() != Link.Type.DIRECT) {
246 continue;
247 }
248 addNeighborAtPort(link.dst().deviceId(), link.src().port());
249 }
250 }
251
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700252 protected void addNeighborAtPort(DeviceId neighborId,
253 PortNumber portToNeighbor) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800254 // Update DeviceToPort database
255 log.debug("Device {} addNeighborAtPort: neighbor {} at port {}",
256 deviceId, neighborId, portToNeighbor);
257 if (devicePortMap.get(neighborId) != null) {
258 devicePortMap.get(neighborId).add(portToNeighbor);
259 } else {
260 Set<PortNumber> ports = new HashSet<PortNumber>();
261 ports.add(portToNeighbor);
262 devicePortMap.put(neighborId, ports);
263 }
264
265 // Update portToDevice database
266 if (portDeviceMap.get(portToNeighbor) == null) {
267 portDeviceMap.put(portToNeighbor, neighborId);
268 }
269 }
270
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700271 protected Set<Set<DeviceId>> getPowerSetOfNeighbors(Set<DeviceId> neighbors) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800272 List<DeviceId> list = new ArrayList<DeviceId>(neighbors);
273 Set<Set<DeviceId>> sets = new HashSet<Set<DeviceId>>();
274 // get the number of elements in the neighbors
275 int elements = list.size();
276 // the number of members of a power set is 2^n
277 // including the empty set
278 int powerElements = (1 << elements);
279
280 // run a binary counter for the number of power elements
281 // NOTE: Exclude empty set
282 for (long i = 1; i < powerElements; i++) {
283 Set<DeviceId> neighborSubSet = new HashSet<DeviceId>();
284 for (int j = 0; j < elements; j++) {
285 if ((i >> j) % 2 == 1) {
286 neighborSubSet.add(list.get(j));
287 }
288 }
289 sets.add(neighborSubSet);
290 }
291 return sets;
292 }
293
294 private boolean isSegmentIdSameAsNodeSegmentId(DeviceId deviceId, int sId) {
295 return (deviceConfig.getSegmentId(deviceId) == sId);
296 }
297
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700298 protected List<Integer> getSegmentIdsTobePairedWithNeighborSet(Set<DeviceId> neighbors) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800299
300 List<Integer> nsSegmentIds = new ArrayList<Integer>();
301
sanghob35a6192015-04-01 13:05:26 -0700302 // Always pair up with no edge label
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700303 // If (neighbors.size() == 1) {
sanghob35a6192015-04-01 13:05:26 -0700304 nsSegmentIds.add(-1);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700305 // }
sanghob35a6192015-04-01 13:05:26 -0700306
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800307 // Filter out SegmentIds matching with the
308 // nodes in the combo
309 for (Integer sId : allSegmentIds) {
310 if (sId.equals(nodeSegmentId)) {
311 continue;
312 }
313 boolean filterOut = false;
314 // Check if the edge label being set is of
315 // any node in the Neighbor set
316 for (DeviceId deviceId : neighbors) {
317 if (isSegmentIdSameAsNodeSegmentId(deviceId, sId)) {
318 filterOut = true;
319 break;
320 }
321 }
322 if (!filterOut) {
323 nsSegmentIds.add(sId);
324 }
325 }
326 return nsSegmentIds;
327 }
328
329 protected void createGroupsFromNeighborsets(Set<NeighborSet> nsSet) {
330 for (NeighborSet ns : nsSet) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700331 int nextId = flowObjectiveService.allocateNextId();
332 NextObjective.Builder nextObjBuilder = DefaultNextObjective
333 .builder().withId(nextId)
334 .withType(NextObjective.Type.HASHED).fromApp(appId);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800335 for (DeviceId d : ns.getDeviceIds()) {
336 for (PortNumber sp : devicePortMap.get(d)) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700337 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
338 .builder();
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800339 tBuilder.setOutput(sp)
340 .setEthDst(deviceConfig.getDeviceMac(d))
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700341 .setEthSrc(nodeMacAddr);
342 if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700343 tBuilder.pushMpls().setMpls(MplsLabel.mplsLabel(ns
344 .getEdgeLabel()));
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700345 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700346 nextObjBuilder.addTreatment(tBuilder.build());
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800347 }
348 }
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800349
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700350 NextObjective nextObj = nextObjBuilder.add();
351 flowObjectiveService.next(deviceId, nextObj);
352 deviceNextObjectiveIds.put(getGroupKey(ns), nextId);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800353 }
354 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700355
sanghob35a6192015-04-01 13:05:26 -0700356 public GroupKey getGroupKey(Object obj) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700357 return new DefaultGroupKey(kryo.build().serialize(obj));
358 }
sanghob35a6192015-04-01 13:05:26 -0700359
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800360}