blob: f5d8ed1707dfaef23ff883accee60269b4485eee [file] [log] [blame]
sanghob35a6192015-04-01 13:05:26 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
sanghob35a6192015-04-01 13:05:26 -07003 *
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.segmentrouting;
17
18import org.onosproject.net.DefaultLink;
19import org.onosproject.net.DefaultPath;
20import org.onosproject.net.Device;
21import org.onosproject.net.DeviceId;
22import org.onosproject.net.Link;
23import org.onosproject.net.Path;
24import org.onosproject.net.provider.ProviderId;
25import org.slf4j.Logger;
26import org.slf4j.LoggerFactory;
27import java.util.ArrayList;
28import java.util.HashMap;
29import java.util.LinkedList;
30import java.util.List;
31
32/**
33 * This class creates bandwidth constrained breadth first tree and returns paths
Saurav Dasd2fded02016-12-02 15:43:47 -080034 * from root Device to leaf Devices (target devices) which satisfies the bandwidth condition. If
sanghob35a6192015-04-01 13:05:26 -070035 * bandwidth parameter is not specified, the normal breadth first tree will be
36 * calculated. The paths are snapshot paths at the point of the class
37 * instantiation.
38 */
Shashikanth VH013a7bc2015-12-11 01:32:44 +053039public class EcmpShortestPathGraph {
sanghob35a6192015-04-01 13:05:26 -070040 LinkedList<DeviceId> deviceQueue = new LinkedList<>();
41 LinkedList<Integer> distanceQueue = new LinkedList<>();
42 HashMap<DeviceId, Integer> deviceSearched = new HashMap<>();
43 HashMap<DeviceId, ArrayList<Link>> upstreamLinks = new HashMap<>();
44 HashMap<DeviceId, ArrayList<Path>> paths = new HashMap<>();
45 HashMap<Integer, ArrayList<DeviceId>> distanceDeviceMap = new HashMap<>();
46 DeviceId rootDevice;
47 private SegmentRoutingManager srManager;
48 private static final Logger log = LoggerFactory
Shashikanth VH013a7bc2015-12-11 01:32:44 +053049 .getLogger(EcmpShortestPathGraph.class);
sanghob35a6192015-04-01 13:05:26 -070050
51 /**
52 * Constructor.
53 *
54 * @param rootDevice root of the BFS tree
55 * @param linkListToAvoid link list to avoid
56 * @param deviceIdListToAvoid device list to avoid
57 */
Shashikanth VH013a7bc2015-12-11 01:32:44 +053058 public EcmpShortestPathGraph(DeviceId rootDevice, List<String> deviceIdListToAvoid,
sanghob35a6192015-04-01 13:05:26 -070059 List<Link> linkListToAvoid) {
60 this.rootDevice = rootDevice;
61 calcECMPShortestPathGraph(deviceIdListToAvoid, linkListToAvoid);
62 }
63
64 /**
65 * Constructor.
66 *
67 * @param rootDevice root of the BFS tree
68 * @param srManager SegmentRoutingManager object
69 */
Shashikanth VH013a7bc2015-12-11 01:32:44 +053070 public EcmpShortestPathGraph(DeviceId rootDevice, SegmentRoutingManager srManager) {
sanghob35a6192015-04-01 13:05:26 -070071 this.rootDevice = rootDevice;
72 this.srManager = srManager;
73 calcECMPShortestPathGraph();
74 }
75
76 /**
77 * Calculates the BFS tree using any provided constraints and Intents.
78 */
79 private void calcECMPShortestPathGraph() {
80 deviceQueue.add(rootDevice);
81 int currDistance = 0;
82 distanceQueue.add(currDistance);
83 deviceSearched.put(rootDevice, currDistance);
84 while (!deviceQueue.isEmpty()) {
85 DeviceId sw = deviceQueue.poll();
86 DeviceId prevSw = null;
87 currDistance = distanceQueue.poll();
88
89 for (Link link : srManager.linkService.getDeviceEgressLinks(sw)) {
90 DeviceId reachedDevice = link.dst().deviceId();
91 if ((prevSw != null)
92 && (prevSw.equals(reachedDevice))) {
93 /* Ignore LAG links between the same set of Devicees */
94 continue;
95 } else {
96 prevSw = reachedDevice;
97 }
98
99 Integer distance = deviceSearched.get(reachedDevice);
Sho SHIMIZUaf973432015-09-11 14:24:50 -0700100 if ((distance != null) && (distance < (currDistance + 1))) {
sanghob35a6192015-04-01 13:05:26 -0700101 continue;
102 }
103 if (distance == null) {
104 /* First time visiting this Device node */
105 deviceQueue.add(reachedDevice);
106 distanceQueue.add(currDistance + 1);
107 deviceSearched.put(reachedDevice, currDistance + 1);
108
109 ArrayList<DeviceId> distanceSwArray = distanceDeviceMap
110 .get(currDistance + 1);
111 if (distanceSwArray == null) {
Sho SHIMIZU6cfc02d2015-09-11 11:19:11 -0700112 distanceSwArray = new ArrayList<>();
sanghob35a6192015-04-01 13:05:26 -0700113 distanceSwArray.add(reachedDevice);
114 distanceDeviceMap.put(currDistance + 1, distanceSwArray);
115 } else {
116 distanceSwArray.add(reachedDevice);
117 }
118 }
119
120 ArrayList<Link> upstreamLinkArray =
121 upstreamLinks.get(reachedDevice);
122 if (upstreamLinkArray == null) {
Sho SHIMIZU6cfc02d2015-09-11 11:19:11 -0700123 upstreamLinkArray = new ArrayList<>();
sanghob35a6192015-04-01 13:05:26 -0700124 upstreamLinkArray.add(copyDefaultLink(link));
125 //upstreamLinkArray.add(link);
126 upstreamLinks.put(reachedDevice, upstreamLinkArray);
127 } else {
128 /* ECMP links */
129 upstreamLinkArray.add(copyDefaultLink(link));
130 }
131 }
132 }
133 }
134
135 /**
136 * Calculates the BFS tree using any provided constraints and Intents.
137 */
138 private void calcECMPShortestPathGraph(List<String> deviceIdListToAvoid, List<Link> linksToAvoid) {
139 deviceQueue.add(rootDevice);
140 int currDistance = 0;
141 distanceQueue.add(currDistance);
142 deviceSearched.put(rootDevice, currDistance);
143 boolean foundLinkToAvoid = false;
144 while (!deviceQueue.isEmpty()) {
145 DeviceId sw = deviceQueue.poll();
146 DeviceId prevSw = null;
147 currDistance = distanceQueue.poll();
148 for (Link link : srManager.linkService.getDeviceEgressLinks(sw)) {
149 for (Link linkToAvoid: linksToAvoid) {
150 // TODO: equls should work
151 //if (link.equals(linkToAvoid)) {
152 if (linkContains(link, linksToAvoid)) {
153 foundLinkToAvoid = true;
154 break;
155 }
156 }
157 if (foundLinkToAvoid) {
158 foundLinkToAvoid = false;
159 continue;
160 }
161 DeviceId reachedDevice = link.dst().deviceId();
162 if (deviceIdListToAvoid.contains(reachedDevice.toString())) {
163 continue;
164 }
165 if ((prevSw != null)
166 && (prevSw.equals(reachedDevice))) {
167 /* Ignore LAG links between the same set of Devicees */
168 continue;
169 } else {
170 prevSw = reachedDevice;
171 }
172
173 Integer distance = deviceSearched.get(reachedDevice);
Sho SHIMIZUaf973432015-09-11 14:24:50 -0700174 if ((distance != null) && (distance < (currDistance + 1))) {
sanghob35a6192015-04-01 13:05:26 -0700175 continue;
176 }
177 if (distance == null) {
178 /* First time visiting this Device node */
179 deviceQueue.add(reachedDevice);
180 distanceQueue.add(currDistance + 1);
181 deviceSearched.put(reachedDevice, currDistance + 1);
182
183 ArrayList<DeviceId> distanceSwArray = distanceDeviceMap
184 .get(currDistance + 1);
185 if (distanceSwArray == null) {
Sho SHIMIZU6cfc02d2015-09-11 11:19:11 -0700186 distanceSwArray = new ArrayList<>();
sanghob35a6192015-04-01 13:05:26 -0700187 distanceSwArray.add(reachedDevice);
188 distanceDeviceMap.put(currDistance + 1, distanceSwArray);
189 } else {
190 distanceSwArray.add(reachedDevice);
191 }
192 }
193
194 ArrayList<Link> upstreamLinkArray =
195 upstreamLinks.get(reachedDevice);
196 if (upstreamLinkArray == null) {
Sho SHIMIZU6cfc02d2015-09-11 11:19:11 -0700197 upstreamLinkArray = new ArrayList<>();
sanghob35a6192015-04-01 13:05:26 -0700198 upstreamLinkArray.add(copyDefaultLink(link));
199 upstreamLinks.put(reachedDevice, upstreamLinkArray);
200 } else {
201 /* ECMP links */
202 upstreamLinkArray.add(copyDefaultLink(link));
203 }
204 }
205 }
206 }
207
208
209 private boolean linkContains(Link link, List<Link> links) {
210
211 DeviceId srcDevice1 = link.src().deviceId();
212 DeviceId dstDevice1 = link.dst().deviceId();
213 long srcPort1 = link.src().port().toLong();
214 long dstPort1 = link.dst().port().toLong();
215
216 for (Link link2: links) {
217 DeviceId srcDevice2 = link2.src().deviceId();
218 DeviceId dstDevice2 = link2.dst().deviceId();
219 long srcPort2 = link2.src().port().toLong();
220 long dstPort2 = link2.dst().port().toLong();
221
222 if (srcDevice1.toString().equals(srcDevice2.toString())
223 && dstDevice1.toString().equals(dstDevice2.toString())
224 && srcPort1 == srcPort2 && dstPort1 == dstPort2) {
225 return true;
226 }
227 }
228
229 return false;
230 }
231
232 private void getDFSPaths(DeviceId dstDeviceDeviceId, Path path, ArrayList<Path> paths) {
233 DeviceId rootDeviceDeviceId = rootDevice;
234 for (Link upstreamLink : upstreamLinks.get(dstDeviceDeviceId)) {
235 /* Deep clone the path object */
236 Path sofarPath;
Sho SHIMIZU6cfc02d2015-09-11 11:19:11 -0700237 ArrayList<Link> sofarLinks = new ArrayList<>();
sanghob35a6192015-04-01 13:05:26 -0700238 if (path != null && !path.links().isEmpty()) {
239 sofarLinks.addAll(path.links());
240 }
241 sofarLinks.add(upstreamLink);
242 sofarPath = new DefaultPath(ProviderId.NONE, sofarLinks, 0);
243 if (upstreamLink.src().deviceId().equals(rootDeviceDeviceId)) {
244 paths.add(sofarPath);
245 return;
246 } else {
247 getDFSPaths(upstreamLink.src().deviceId(), sofarPath, paths);
248 }
249 }
250 }
251
252 /**
253 * Return root Device for the graph.
254 *
255 * @return root Device
256 */
257 public DeviceId getRootDevice() {
258 return rootDevice;
259 }
260
261 /**
262 * Return the computed ECMP paths from the root Device to a given Device in
263 * the network.
264 *
265 * @param targetDevice the target Device
266 * @return the list of ECMP Paths from the root Device to the target Device
267 */
268 public ArrayList<Path> getECMPPaths(DeviceId targetDevice) {
269 ArrayList<Path> pathArray = paths.get(targetDevice);
270 if (pathArray == null && deviceSearched.containsKey(
271 targetDevice)) {
272 pathArray = new ArrayList<>();
273 DeviceId sw = targetDevice;
274 getDFSPaths(sw, null, pathArray);
275 paths.put(targetDevice, pathArray);
276 }
277 return pathArray;
278 }
279
280 /**
281 * Return the complete info of the computed ECMP paths for each Device
282 * learned in multiple iterations from the root Device.
283 *
Saurav Dasa07f2032015-10-19 14:37:36 -0700284 * @return the hash table of Devices learned in multiple Dijkstra
sanghob35a6192015-04-01 13:05:26 -0700285 * iterations and corresponding ECMP paths to it from the root
286 * Device
287 */
288 public HashMap<Integer, HashMap<DeviceId,
289 ArrayList<Path>>> getCompleteLearnedDeviceesAndPaths() {
290
Sho SHIMIZU6cfc02d2015-09-11 11:19:11 -0700291 HashMap<Integer, HashMap<DeviceId, ArrayList<Path>>> pathGraph = new HashMap<>();
sanghob35a6192015-04-01 13:05:26 -0700292
293 for (Integer itrIndx : distanceDeviceMap.keySet()) {
Sho SHIMIZU6cfc02d2015-09-11 11:19:11 -0700294 HashMap<DeviceId, ArrayList<Path>> swMap = new HashMap<>();
sanghob35a6192015-04-01 13:05:26 -0700295 for (DeviceId sw : distanceDeviceMap.get(itrIndx)) {
296 swMap.put(sw, getECMPPaths(sw));
297 }
298 pathGraph.put(itrIndx, swMap);
299 }
300
301 return pathGraph;
302 }
303
304 /**
Saurav Dasd2fded02016-12-02 15:43:47 -0800305 * Returns the complete info of the computed ECMP paths for each target device
Saurav Das4e3224f2016-11-29 14:27:25 -0800306 * learned in multiple iterations from the root Device. The computed info
307 * returned is per iteration (Integer key of outer HashMap). In each
Saurav Dasd2fded02016-12-02 15:43:47 -0800308 * iteration, for the target devices reached (DeviceId key of inner HashMap),
Saurav Das4e3224f2016-11-29 14:27:25 -0800309 * the ECMP paths are detailed (2D array).
sanghob35a6192015-04-01 13:05:26 -0700310 *
Saurav Dasd2fded02016-12-02 15:43:47 -0800311 * @return the hash table of target Devices learned in multiple Dijkstra
Saurav Dasa07f2032015-10-19 14:37:36 -0700312 * iterations and corresponding ECMP paths in terms of Devices to
Saurav Dasd2fded02016-12-02 15:43:47 -0800313 * be traversed (via) from the root Device to the target Device
sanghob35a6192015-04-01 13:05:26 -0700314 */
315 public HashMap<Integer, HashMap<DeviceId,
316 ArrayList<ArrayList<DeviceId>>>> getAllLearnedSwitchesAndVia() {
317
Sho SHIMIZU6cfc02d2015-09-11 11:19:11 -0700318 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> deviceViaMap = new HashMap<>();
sanghob35a6192015-04-01 13:05:26 -0700319
320 for (Integer itrIndx : distanceDeviceMap.keySet()) {
Sho SHIMIZU6cfc02d2015-09-11 11:19:11 -0700321 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swMap = new HashMap<>();
sanghob35a6192015-04-01 13:05:26 -0700322
323 for (DeviceId sw : distanceDeviceMap.get(itrIndx)) {
324 ArrayList<ArrayList<DeviceId>> swViaArray = new ArrayList<>();
325 for (Path path : getECMPPaths(sw)) {
326 ArrayList<DeviceId> swVia = new ArrayList<>();
327 for (Link link : path.links()) {
328 if (link.src().deviceId().equals(rootDevice)) {
329 /* No need to add the root Device again in
330 * the Via list
331 */
332 continue;
333 }
334 swVia.add(link.src().deviceId());
335 }
336 swViaArray.add(swVia);
337 }
338 swMap.put(sw, swViaArray);
339 }
340 deviceViaMap.put(itrIndx, swMap);
341 }
342 return deviceViaMap;
343 }
344
345
346 private Link copyDefaultLink(Link link) {
347 DefaultLink src = (DefaultLink) link;
Ray Milkey2693bda2016-01-22 16:08:14 -0800348 DefaultLink defaultLink = DefaultLink.builder()
349 .providerId(src.providerId())
350 .src(src.src())
351 .dst(src.dst())
352 .type(src.type())
353 .annotations(src.annotations())
354 .build();
sanghob35a6192015-04-01 13:05:26 -0700355
356 return defaultLink;
357 }
358
359 @Override
360 public String toString() {
361 StringBuilder sBuilder = new StringBuilder();
362 for (Device device: srManager.deviceService.getDevices()) {
363 if (device.id() != rootDevice) {
Saurav Das4e3224f2016-11-29 14:27:25 -0800364 sBuilder.append("\r\n Paths from " + rootDevice + " to "
365 + device.id() + "\r\n");
sanghob35a6192015-04-01 13:05:26 -0700366 ArrayList<Path> paths = getECMPPaths(device.id());
367 if (paths != null) {
368 for (Path path : paths) {
Saurav Das4e3224f2016-11-29 14:27:25 -0800369 sBuilder.append("\r\n == "); // equal cost paths delimiter
sanghob35a6192015-04-01 13:05:26 -0700370 for (Link link : path.links()) {
371 sBuilder.append(" : " + link.src() + " -> " + link.dst());
372 }
373 }
374 }
375 }
376 }
377 return sBuilder.toString();
378 }
379}
380