blob: 036044cbb512153e172f24d9ee41b3f657029de2 [file] [log] [blame]
sangho80f11cb2015-04-01 13:05:26 -07001/*
Brian O'Connor0947d7e2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
sangho80f11cb2015-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
Ray Milkeydd9d00b2018-02-08 15:07:06 -080018import org.onlab.graph.ScalarWeight;
sangho80f11cb2015-04-01 13:05:26 -070019import org.onosproject.net.DefaultLink;
20import org.onosproject.net.DefaultPath;
21import org.onosproject.net.Device;
22import org.onosproject.net.DeviceId;
23import org.onosproject.net.Link;
24import org.onosproject.net.Path;
25import org.onosproject.net.provider.ProviderId;
26import org.slf4j.Logger;
27import org.slf4j.LoggerFactory;
Saurav Das261c3002017-06-13 15:35:54 -070028
29import com.google.common.collect.Sets;
30
sangho80f11cb2015-04-01 13:05:26 -070031import java.util.ArrayList;
32import java.util.HashMap;
33import java.util.LinkedList;
Saurav Das261c3002017-06-13 15:35:54 -070034import java.util.Set;
sangho80f11cb2015-04-01 13:05:26 -070035
36/**
Saurav Das261c3002017-06-13 15:35:54 -070037 * This class creates breadth-first-search (BFS) tree for a given root device
38 * and returns paths from the root Device to leaf Devices (target devices).
39 * The paths are snapshot paths at the point of the class instantiation.
sangho80f11cb2015-04-01 13:05:26 -070040 */
Shashikanth VH0637b162015-12-11 01:32:44 +053041public class EcmpShortestPathGraph {
sangho80f11cb2015-04-01 13:05:26 -070042 LinkedList<DeviceId> deviceQueue = new LinkedList<>();
43 LinkedList<Integer> distanceQueue = new LinkedList<>();
44 HashMap<DeviceId, Integer> deviceSearched = new HashMap<>();
45 HashMap<DeviceId, ArrayList<Link>> upstreamLinks = new HashMap<>();
46 HashMap<DeviceId, ArrayList<Path>> paths = new HashMap<>();
47 HashMap<Integer, ArrayList<DeviceId>> distanceDeviceMap = new HashMap<>();
48 DeviceId rootDevice;
49 private SegmentRoutingManager srManager;
Saurav Das261c3002017-06-13 15:35:54 -070050 private static final Logger log = LoggerFactory.getLogger(EcmpShortestPathGraph.class);
sangho80f11cb2015-04-01 13:05:26 -070051
52 /**
53 * Constructor.
54 *
55 * @param rootDevice root of the BFS tree
56 * @param srManager SegmentRoutingManager object
57 */
Shashikanth VH0637b162015-12-11 01:32:44 +053058 public EcmpShortestPathGraph(DeviceId rootDevice, SegmentRoutingManager srManager) {
sangho80f11cb2015-04-01 13:05:26 -070059 this.rootDevice = rootDevice;
60 this.srManager = srManager;
61 calcECMPShortestPathGraph();
62 }
63
64 /**
Saurav Das261c3002017-06-13 15:35:54 -070065 * Calculates the BFS tree.
sangho80f11cb2015-04-01 13:05:26 -070066 */
Saurav Das261c3002017-06-13 15:35:54 -070067 private void calcECMPShortestPathGraph() {
sangho80f11cb2015-04-01 13:05:26 -070068 deviceQueue.add(rootDevice);
69 int currDistance = 0;
70 distanceQueue.add(currDistance);
71 deviceSearched.put(rootDevice, currDistance);
72 while (!deviceQueue.isEmpty()) {
73 DeviceId sw = deviceQueue.poll();
Saurav Das261c3002017-06-13 15:35:54 -070074 Set<DeviceId> prevSw = Sets.newHashSet();
sangho80f11cb2015-04-01 13:05:26 -070075 currDistance = distanceQueue.poll();
76
Saurav Dasdc7f2752018-03-18 21:28:15 -070077 for (Link link : srManager.linkHandler.getDeviceEgressLinks(sw)) {
Saurav Dase6c448a2018-01-18 12:07:33 -080078 if (srManager.linkHandler.avoidLink(link)) {
Saurav Das261c3002017-06-13 15:35:54 -070079 continue;
80 }
sangho80f11cb2015-04-01 13:05:26 -070081 DeviceId reachedDevice = link.dst().deviceId();
Saurav Das261c3002017-06-13 15:35:54 -070082 if (prevSw.contains(reachedDevice)) {
83 // Ignore LAG links between the same set of Devices
sangho80f11cb2015-04-01 13:05:26 -070084 continue;
85 } else {
Saurav Das261c3002017-06-13 15:35:54 -070086 prevSw.add(reachedDevice);
sangho80f11cb2015-04-01 13:05:26 -070087 }
88
89 Integer distance = deviceSearched.get(reachedDevice);
Sho SHIMIZUd1793352015-09-11 14:24:50 -070090 if ((distance != null) && (distance < (currDistance + 1))) {
sangho80f11cb2015-04-01 13:05:26 -070091 continue;
92 }
93 if (distance == null) {
Saurav Das261c3002017-06-13 15:35:54 -070094 // First time visiting this Device node
sangho80f11cb2015-04-01 13:05:26 -070095 deviceQueue.add(reachedDevice);
96 distanceQueue.add(currDistance + 1);
97 deviceSearched.put(reachedDevice, currDistance + 1);
98
99 ArrayList<DeviceId> distanceSwArray = distanceDeviceMap
100 .get(currDistance + 1);
101 if (distanceSwArray == null) {
Sho SHIMIZU47b8aa22015-09-11 11:19:11 -0700102 distanceSwArray = new ArrayList<>();
sangho80f11cb2015-04-01 13:05:26 -0700103 distanceSwArray.add(reachedDevice);
104 distanceDeviceMap.put(currDistance + 1, distanceSwArray);
105 } else {
106 distanceSwArray.add(reachedDevice);
107 }
108 }
109
110 ArrayList<Link> upstreamLinkArray =
111 upstreamLinks.get(reachedDevice);
112 if (upstreamLinkArray == null) {
Sho SHIMIZU47b8aa22015-09-11 11:19:11 -0700113 upstreamLinkArray = new ArrayList<>();
sangho80f11cb2015-04-01 13:05:26 -0700114 upstreamLinkArray.add(copyDefaultLink(link));
115 //upstreamLinkArray.add(link);
116 upstreamLinks.put(reachedDevice, upstreamLinkArray);
117 } else {
Saurav Das261c3002017-06-13 15:35:54 -0700118 // ECMP links
sangho80f11cb2015-04-01 13:05:26 -0700119 upstreamLinkArray.add(copyDefaultLink(link));
120 }
121 }
122 }
123 }
124
sangho80f11cb2015-04-01 13:05:26 -0700125 private void getDFSPaths(DeviceId dstDeviceDeviceId, Path path, ArrayList<Path> paths) {
126 DeviceId rootDeviceDeviceId = rootDevice;
127 for (Link upstreamLink : upstreamLinks.get(dstDeviceDeviceId)) {
128 /* Deep clone the path object */
129 Path sofarPath;
Sho SHIMIZU47b8aa22015-09-11 11:19:11 -0700130 ArrayList<Link> sofarLinks = new ArrayList<>();
sangho80f11cb2015-04-01 13:05:26 -0700131 if (path != null && !path.links().isEmpty()) {
132 sofarLinks.addAll(path.links());
133 }
134 sofarLinks.add(upstreamLink);
Ray Milkeydd9d00b2018-02-08 15:07:06 -0800135 sofarPath = new DefaultPath(ProviderId.NONE, sofarLinks, ScalarWeight.toWeight(0));
sangho80f11cb2015-04-01 13:05:26 -0700136 if (upstreamLink.src().deviceId().equals(rootDeviceDeviceId)) {
137 paths.add(sofarPath);
138 return;
139 } else {
140 getDFSPaths(upstreamLink.src().deviceId(), sofarPath, paths);
141 }
142 }
143 }
144
145 /**
146 * Return root Device for the graph.
147 *
148 * @return root Device
149 */
150 public DeviceId getRootDevice() {
151 return rootDevice;
152 }
153
154 /**
155 * Return the computed ECMP paths from the root Device to a given Device in
156 * the network.
157 *
158 * @param targetDevice the target Device
159 * @return the list of ECMP Paths from the root Device to the target Device
160 */
161 public ArrayList<Path> getECMPPaths(DeviceId targetDevice) {
162 ArrayList<Path> pathArray = paths.get(targetDevice);
163 if (pathArray == null && deviceSearched.containsKey(
164 targetDevice)) {
165 pathArray = new ArrayList<>();
166 DeviceId sw = targetDevice;
167 getDFSPaths(sw, null, pathArray);
168 paths.put(targetDevice, pathArray);
169 }
170 return pathArray;
171 }
172
173 /**
174 * Return the complete info of the computed ECMP paths for each Device
175 * learned in multiple iterations from the root Device.
176 *
Saurav Das88979182015-10-19 14:37:36 -0700177 * @return the hash table of Devices learned in multiple Dijkstra
sangho80f11cb2015-04-01 13:05:26 -0700178 * iterations and corresponding ECMP paths to it from the root
179 * Device
180 */
181 public HashMap<Integer, HashMap<DeviceId,
182 ArrayList<Path>>> getCompleteLearnedDeviceesAndPaths() {
183
Sho SHIMIZU47b8aa22015-09-11 11:19:11 -0700184 HashMap<Integer, HashMap<DeviceId, ArrayList<Path>>> pathGraph = new HashMap<>();
sangho80f11cb2015-04-01 13:05:26 -0700185
186 for (Integer itrIndx : distanceDeviceMap.keySet()) {
Sho SHIMIZU47b8aa22015-09-11 11:19:11 -0700187 HashMap<DeviceId, ArrayList<Path>> swMap = new HashMap<>();
sangho80f11cb2015-04-01 13:05:26 -0700188 for (DeviceId sw : distanceDeviceMap.get(itrIndx)) {
189 swMap.put(sw, getECMPPaths(sw));
190 }
191 pathGraph.put(itrIndx, swMap);
192 }
193
194 return pathGraph;
195 }
196
197 /**
Saurav Dasd1872b02016-12-02 15:43:47 -0800198 * Returns the complete info of the computed ECMP paths for each target device
Saurav Das1b391d52016-11-29 14:27:25 -0800199 * learned in multiple iterations from the root Device. The computed info
200 * returned is per iteration (Integer key of outer HashMap). In each
Saurav Dasd1872b02016-12-02 15:43:47 -0800201 * iteration, for the target devices reached (DeviceId key of inner HashMap),
Saurav Das1b391d52016-11-29 14:27:25 -0800202 * the ECMP paths are detailed (2D array).
sangho80f11cb2015-04-01 13:05:26 -0700203 *
Saurav Dasd1872b02016-12-02 15:43:47 -0800204 * @return the hash table of target Devices learned in multiple Dijkstra
Saurav Das88979182015-10-19 14:37:36 -0700205 * iterations and corresponding ECMP paths in terms of Devices to
Saurav Dasd1872b02016-12-02 15:43:47 -0800206 * be traversed (via) from the root Device to the target Device
sangho80f11cb2015-04-01 13:05:26 -0700207 */
208 public HashMap<Integer, HashMap<DeviceId,
209 ArrayList<ArrayList<DeviceId>>>> getAllLearnedSwitchesAndVia() {
210
Sho SHIMIZU47b8aa22015-09-11 11:19:11 -0700211 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> deviceViaMap = new HashMap<>();
sangho80f11cb2015-04-01 13:05:26 -0700212
213 for (Integer itrIndx : distanceDeviceMap.keySet()) {
Sho SHIMIZU47b8aa22015-09-11 11:19:11 -0700214 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swMap = new HashMap<>();
sangho80f11cb2015-04-01 13:05:26 -0700215
216 for (DeviceId sw : distanceDeviceMap.get(itrIndx)) {
217 ArrayList<ArrayList<DeviceId>> swViaArray = new ArrayList<>();
218 for (Path path : getECMPPaths(sw)) {
219 ArrayList<DeviceId> swVia = new ArrayList<>();
220 for (Link link : path.links()) {
221 if (link.src().deviceId().equals(rootDevice)) {
222 /* No need to add the root Device again in
223 * the Via list
224 */
225 continue;
226 }
227 swVia.add(link.src().deviceId());
228 }
229 swViaArray.add(swVia);
230 }
231 swMap.put(sw, swViaArray);
232 }
233 deviceViaMap.put(itrIndx, swMap);
234 }
235 return deviceViaMap;
236 }
237
238
239 private Link copyDefaultLink(Link link) {
240 DefaultLink src = (DefaultLink) link;
Ray Milkey5e1d0fc2016-01-22 16:08:14 -0800241 DefaultLink defaultLink = DefaultLink.builder()
242 .providerId(src.providerId())
243 .src(src.src())
244 .dst(src.dst())
245 .type(src.type())
246 .annotations(src.annotations())
247 .build();
sangho80f11cb2015-04-01 13:05:26 -0700248
249 return defaultLink;
250 }
251
252 @Override
253 public String toString() {
254 StringBuilder sBuilder = new StringBuilder();
255 for (Device device: srManager.deviceService.getDevices()) {
Saurav Das261c3002017-06-13 15:35:54 -0700256 if (!device.id().equals(rootDevice)) {
Saurav Das62ae6792017-05-15 15:34:25 -0700257 sBuilder.append("\r\n Paths from " + rootDevice + " to "
258 + device.id());
sangho80f11cb2015-04-01 13:05:26 -0700259 ArrayList<Path> paths = getECMPPaths(device.id());
260 if (paths != null) {
261 for (Path path : paths) {
Saurav Das62ae6792017-05-15 15:34:25 -0700262 sBuilder.append("\r\n == "); // equal cost paths delimiter
263 for (int i = path.links().size() - 1; i >= 0; i--) {
264 Link link = path.links().get(i);
sangho80f11cb2015-04-01 13:05:26 -0700265 sBuilder.append(" : " + link.src() + " -> " + link.dst());
266 }
267 }
Saurav Das62ae6792017-05-15 15:34:25 -0700268 } else {
269 sBuilder.append("\r\n == no paths");
sangho80f11cb2015-04-01 13:05:26 -0700270 }
271 }
272 }
273 return sBuilder.toString();
274 }
275}
276