blob: 1d9f4ecd46779684734011bd5e7d29a1ffb7cbad [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 }
sangho834e4b02015-05-01 09:38:25 -0700189
190 Integer nextId = deviceNextObjectiveIds.get(getGroupKey(ns));
191 if (nextId != null) {
192 NextObjective.Builder nextObjBuilder = DefaultNextObjective
193 .builder().withType(NextObjective.Type.SIMPLE).withId(nextId).fromApp(appId);
194
195 nextObjBuilder.addTreatment(tBuilder.build());
196
197 NextObjective nextObjective = nextObjBuilder.remove();
198
199 flowObjectiveService.next(deviceId, nextObjective);
200 }
201
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800202 }
203
204 devicePortMap.get(portDeviceMap.get(port)).remove(port);
205 portDeviceMap.remove(port);
206 }
207
208 /**
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700209 * Returns the next objective associated with the neighborset.
210 * If there is no next objective for this neighborset, this API
211 * would create a next objective and return.
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800212 *
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700213 * @param ns neighborset
214 * @return int if found or -1
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800215 */
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700216 public int getNextObjectiveId(NeighborSet ns) {
217 Integer nextId = deviceNextObjectiveIds.get(getGroupKey(ns));
218 if (nextId == null) {
219 createGroupsFromNeighborsets(Collections.singleton(ns));
220 nextId = deviceNextObjectiveIds.get(getGroupKey(ns));
221 if (nextId == null) {
222 log.warn("getNextObjectiveId: unable to create next objective");
223 return -1;
224 }
225 }
226 return nextId.intValue();
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800227 }
228
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700229 // Empty implementation
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800230 protected void newNeighbor(Link newLink) {
231 }
232
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700233 // Empty implementation
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800234 protected void newPortToExistingNeighbor(Link newLink) {
235 }
236
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700237 // Empty implementation
238 protected Set<NeighborSet>
239 computeImpactedNeighborsetForPortEvent(DeviceId impactedNeighbor,
240 Set<DeviceId> updatedNeighbors) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800241 return null;
242 }
243
244 private void populateNeighborMaps() {
245 Set<Link> outgoingLinks = linkService.getDeviceEgressLinks(deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700246 for (Link link : outgoingLinks) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800247 if (link.type() != Link.Type.DIRECT) {
248 continue;
249 }
250 addNeighborAtPort(link.dst().deviceId(), link.src().port());
251 }
252 }
253
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700254 protected void addNeighborAtPort(DeviceId neighborId,
255 PortNumber portToNeighbor) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800256 // Update DeviceToPort database
257 log.debug("Device {} addNeighborAtPort: neighbor {} at port {}",
258 deviceId, neighborId, portToNeighbor);
259 if (devicePortMap.get(neighborId) != null) {
260 devicePortMap.get(neighborId).add(portToNeighbor);
261 } else {
262 Set<PortNumber> ports = new HashSet<PortNumber>();
263 ports.add(portToNeighbor);
264 devicePortMap.put(neighborId, ports);
265 }
266
267 // Update portToDevice database
268 if (portDeviceMap.get(portToNeighbor) == null) {
269 portDeviceMap.put(portToNeighbor, neighborId);
270 }
271 }
272
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700273 protected Set<Set<DeviceId>> getPowerSetOfNeighbors(Set<DeviceId> neighbors) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800274 List<DeviceId> list = new ArrayList<DeviceId>(neighbors);
275 Set<Set<DeviceId>> sets = new HashSet<Set<DeviceId>>();
276 // get the number of elements in the neighbors
277 int elements = list.size();
278 // the number of members of a power set is 2^n
279 // including the empty set
280 int powerElements = (1 << elements);
281
282 // run a binary counter for the number of power elements
283 // NOTE: Exclude empty set
284 for (long i = 1; i < powerElements; i++) {
285 Set<DeviceId> neighborSubSet = new HashSet<DeviceId>();
286 for (int j = 0; j < elements; j++) {
287 if ((i >> j) % 2 == 1) {
288 neighborSubSet.add(list.get(j));
289 }
290 }
291 sets.add(neighborSubSet);
292 }
293 return sets;
294 }
295
296 private boolean isSegmentIdSameAsNodeSegmentId(DeviceId deviceId, int sId) {
297 return (deviceConfig.getSegmentId(deviceId) == sId);
298 }
299
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700300 protected List<Integer> getSegmentIdsTobePairedWithNeighborSet(Set<DeviceId> neighbors) {
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800301
302 List<Integer> nsSegmentIds = new ArrayList<Integer>();
303
sanghob35a6192015-04-01 13:05:26 -0700304 // Always pair up with no edge label
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700305 // If (neighbors.size() == 1) {
sanghob35a6192015-04-01 13:05:26 -0700306 nsSegmentIds.add(-1);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700307 // }
sanghob35a6192015-04-01 13:05:26 -0700308
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800309 // Filter out SegmentIds matching with the
310 // nodes in the combo
311 for (Integer sId : allSegmentIds) {
312 if (sId.equals(nodeSegmentId)) {
313 continue;
314 }
315 boolean filterOut = false;
316 // Check if the edge label being set is of
317 // any node in the Neighbor set
318 for (DeviceId deviceId : neighbors) {
319 if (isSegmentIdSameAsNodeSegmentId(deviceId, sId)) {
320 filterOut = true;
321 break;
322 }
323 }
324 if (!filterOut) {
325 nsSegmentIds.add(sId);
326 }
327 }
328 return nsSegmentIds;
329 }
330
331 protected void createGroupsFromNeighborsets(Set<NeighborSet> nsSet) {
332 for (NeighborSet ns : nsSet) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700333 int nextId = flowObjectiveService.allocateNextId();
334 NextObjective.Builder nextObjBuilder = DefaultNextObjective
335 .builder().withId(nextId)
336 .withType(NextObjective.Type.HASHED).fromApp(appId);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800337 for (DeviceId d : ns.getDeviceIds()) {
sangho834e4b02015-05-01 09:38:25 -0700338 if (devicePortMap.get(d) == null) {
339 log.warn("Device {} is not in the port map yet", d);
340 return;
341 }
342
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800343 for (PortNumber sp : devicePortMap.get(d)) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700344 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
345 .builder();
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800346 tBuilder.setOutput(sp)
347 .setEthDst(deviceConfig.getDeviceMac(d))
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700348 .setEthSrc(nodeMacAddr);
349 if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700350 tBuilder.pushMpls().setMpls(MplsLabel.mplsLabel(ns
sangho834e4b02015-05-01 09:38:25 -0700351 .getEdgeLabel()));
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700352 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700353 nextObjBuilder.addTreatment(tBuilder.build());
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800354 }
355 }
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800356
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700357 NextObjective nextObj = nextObjBuilder.add();
358 flowObjectiveService.next(deviceId, nextObj);
359 deviceNextObjectiveIds.put(getGroupKey(ns), nextId);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800360 }
361 }
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700362
sanghob35a6192015-04-01 13:05:26 -0700363 public GroupKey getGroupKey(Object obj) {
Srikanth Vavilapalli717361f2015-03-16 12:06:04 -0700364 return new DefaultGroupKey(kryo.build().serialize(obj));
365 }
sanghob35a6192015-04-01 13:05:26 -0700366
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800367}