blob: 49bf80f4d3c881bc606e16ff8b2f889ad94077c7 [file] [log] [blame]
sanghob35a6192015-04-01 13:05:26 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
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;
Saurav Das7bcbe702017-06-13 15:35:54 -070027
28import com.google.common.collect.Sets;
29
sanghob35a6192015-04-01 13:05:26 -070030import java.util.ArrayList;
31import java.util.HashMap;
32import java.util.LinkedList;
Saurav Das7bcbe702017-06-13 15:35:54 -070033import java.util.Set;
sanghob35a6192015-04-01 13:05:26 -070034
35/**
Saurav Das7bcbe702017-06-13 15:35:54 -070036 * This class creates breadth-first-search (BFS) tree for a given root device
37 * and returns paths from the root Device to leaf Devices (target devices).
38 * The paths are snapshot paths at the point of the class instantiation.
sanghob35a6192015-04-01 13:05:26 -070039 */
Shashikanth VH013a7bc2015-12-11 01:32:44 +053040public class EcmpShortestPathGraph {
sanghob35a6192015-04-01 13:05:26 -070041 LinkedList<DeviceId> deviceQueue = new LinkedList<>();
42 LinkedList<Integer> distanceQueue = new LinkedList<>();
43 HashMap<DeviceId, Integer> deviceSearched = new HashMap<>();
44 HashMap<DeviceId, ArrayList<Link>> upstreamLinks = new HashMap<>();
45 HashMap<DeviceId, ArrayList<Path>> paths = new HashMap<>();
46 HashMap<Integer, ArrayList<DeviceId>> distanceDeviceMap = new HashMap<>();
47 DeviceId rootDevice;
48 private SegmentRoutingManager srManager;
Saurav Das7bcbe702017-06-13 15:35:54 -070049 private static final Logger log = LoggerFactory.getLogger(EcmpShortestPathGraph.class);
sanghob35a6192015-04-01 13:05:26 -070050
51 /**
52 * Constructor.
53 *
54 * @param rootDevice root of the BFS tree
55 * @param srManager SegmentRoutingManager object
56 */
Shashikanth VH013a7bc2015-12-11 01:32:44 +053057 public EcmpShortestPathGraph(DeviceId rootDevice, SegmentRoutingManager srManager) {
sanghob35a6192015-04-01 13:05:26 -070058 this.rootDevice = rootDevice;
59 this.srManager = srManager;
60 calcECMPShortestPathGraph();
61 }
62
63 /**
Saurav Das7bcbe702017-06-13 15:35:54 -070064 * Calculates the BFS tree.
sanghob35a6192015-04-01 13:05:26 -070065 */
Saurav Das7bcbe702017-06-13 15:35:54 -070066 private void calcECMPShortestPathGraph() {
sanghob35a6192015-04-01 13:05:26 -070067 deviceQueue.add(rootDevice);
68 int currDistance = 0;
69 distanceQueue.add(currDistance);
70 deviceSearched.put(rootDevice, currDistance);
71 while (!deviceQueue.isEmpty()) {
72 DeviceId sw = deviceQueue.poll();
Saurav Das7bcbe702017-06-13 15:35:54 -070073 Set<DeviceId> prevSw = Sets.newHashSet();
sanghob35a6192015-04-01 13:05:26 -070074 currDistance = distanceQueue.poll();
75
76 for (Link link : srManager.linkService.getDeviceEgressLinks(sw)) {
Saurav Dase9c8971e2018-01-18 12:07:33 -080077 if (srManager.linkHandler.avoidLink(link)) {
Saurav Das7bcbe702017-06-13 15:35:54 -070078 continue;
79 }
sanghob35a6192015-04-01 13:05:26 -070080 DeviceId reachedDevice = link.dst().deviceId();
Saurav Das7bcbe702017-06-13 15:35:54 -070081 if (prevSw.contains(reachedDevice)) {
82 // Ignore LAG links between the same set of Devices
sanghob35a6192015-04-01 13:05:26 -070083 continue;
84 } else {
Saurav Das7bcbe702017-06-13 15:35:54 -070085 prevSw.add(reachedDevice);
sanghob35a6192015-04-01 13:05:26 -070086 }
87
88 Integer distance = deviceSearched.get(reachedDevice);
Sho SHIMIZUaf973432015-09-11 14:24:50 -070089 if ((distance != null) && (distance < (currDistance + 1))) {
sanghob35a6192015-04-01 13:05:26 -070090 continue;
91 }
92 if (distance == null) {
Saurav Das7bcbe702017-06-13 15:35:54 -070093 // First time visiting this Device node
sanghob35a6192015-04-01 13:05:26 -070094 deviceQueue.add(reachedDevice);
95 distanceQueue.add(currDistance + 1);
96 deviceSearched.put(reachedDevice, currDistance + 1);
97
98 ArrayList<DeviceId> distanceSwArray = distanceDeviceMap
99 .get(currDistance + 1);
100 if (distanceSwArray == null) {
Sho SHIMIZU6cfc02d2015-09-11 11:19:11 -0700101 distanceSwArray = new ArrayList<>();
sanghob35a6192015-04-01 13:05:26 -0700102 distanceSwArray.add(reachedDevice);
103 distanceDeviceMap.put(currDistance + 1, distanceSwArray);
104 } else {
105 distanceSwArray.add(reachedDevice);
106 }
107 }
108
109 ArrayList<Link> upstreamLinkArray =
110 upstreamLinks.get(reachedDevice);
111 if (upstreamLinkArray == null) {
Sho SHIMIZU6cfc02d2015-09-11 11:19:11 -0700112 upstreamLinkArray = new ArrayList<>();
sanghob35a6192015-04-01 13:05:26 -0700113 upstreamLinkArray.add(copyDefaultLink(link));
114 //upstreamLinkArray.add(link);
115 upstreamLinks.put(reachedDevice, upstreamLinkArray);
116 } else {
Saurav Das7bcbe702017-06-13 15:35:54 -0700117 // ECMP links
sanghob35a6192015-04-01 13:05:26 -0700118 upstreamLinkArray.add(copyDefaultLink(link));
119 }
120 }
121 }
122 }
123
sanghob35a6192015-04-01 13:05:26 -0700124 private void getDFSPaths(DeviceId dstDeviceDeviceId, Path path, ArrayList<Path> paths) {
125 DeviceId rootDeviceDeviceId = rootDevice;
126 for (Link upstreamLink : upstreamLinks.get(dstDeviceDeviceId)) {
127 /* Deep clone the path object */
128 Path sofarPath;
Sho SHIMIZU6cfc02d2015-09-11 11:19:11 -0700129 ArrayList<Link> sofarLinks = new ArrayList<>();
sanghob35a6192015-04-01 13:05:26 -0700130 if (path != null && !path.links().isEmpty()) {
131 sofarLinks.addAll(path.links());
132 }
133 sofarLinks.add(upstreamLink);
134 sofarPath = new DefaultPath(ProviderId.NONE, sofarLinks, 0);
135 if (upstreamLink.src().deviceId().equals(rootDeviceDeviceId)) {
136 paths.add(sofarPath);
137 return;
138 } else {
139 getDFSPaths(upstreamLink.src().deviceId(), sofarPath, paths);
140 }
141 }
142 }
143
144 /**
145 * Return root Device for the graph.
146 *
147 * @return root Device
148 */
149 public DeviceId getRootDevice() {
150 return rootDevice;
151 }
152
153 /**
154 * Return the computed ECMP paths from the root Device to a given Device in
155 * the network.
156 *
157 * @param targetDevice the target Device
158 * @return the list of ECMP Paths from the root Device to the target Device
159 */
160 public ArrayList<Path> getECMPPaths(DeviceId targetDevice) {
161 ArrayList<Path> pathArray = paths.get(targetDevice);
162 if (pathArray == null && deviceSearched.containsKey(
163 targetDevice)) {
164 pathArray = new ArrayList<>();
165 DeviceId sw = targetDevice;
166 getDFSPaths(sw, null, pathArray);
167 paths.put(targetDevice, pathArray);
168 }
169 return pathArray;
170 }
171
172 /**
173 * Return the complete info of the computed ECMP paths for each Device
174 * learned in multiple iterations from the root Device.
175 *
Saurav Dasa07f2032015-10-19 14:37:36 -0700176 * @return the hash table of Devices learned in multiple Dijkstra
sanghob35a6192015-04-01 13:05:26 -0700177 * iterations and corresponding ECMP paths to it from the root
178 * Device
179 */
180 public HashMap<Integer, HashMap<DeviceId,
181 ArrayList<Path>>> getCompleteLearnedDeviceesAndPaths() {
182
Sho SHIMIZU6cfc02d2015-09-11 11:19:11 -0700183 HashMap<Integer, HashMap<DeviceId, ArrayList<Path>>> pathGraph = new HashMap<>();
sanghob35a6192015-04-01 13:05:26 -0700184
185 for (Integer itrIndx : distanceDeviceMap.keySet()) {
Sho SHIMIZU6cfc02d2015-09-11 11:19:11 -0700186 HashMap<DeviceId, ArrayList<Path>> swMap = new HashMap<>();
sanghob35a6192015-04-01 13:05:26 -0700187 for (DeviceId sw : distanceDeviceMap.get(itrIndx)) {
188 swMap.put(sw, getECMPPaths(sw));
189 }
190 pathGraph.put(itrIndx, swMap);
191 }
192
193 return pathGraph;
194 }
195
196 /**
Saurav Dasd2fded02016-12-02 15:43:47 -0800197 * Returns the complete info of the computed ECMP paths for each target device
Saurav Das4e3224f2016-11-29 14:27:25 -0800198 * learned in multiple iterations from the root Device. The computed info
199 * returned is per iteration (Integer key of outer HashMap). In each
Saurav Dasd2fded02016-12-02 15:43:47 -0800200 * iteration, for the target devices reached (DeviceId key of inner HashMap),
Saurav Das4e3224f2016-11-29 14:27:25 -0800201 * the ECMP paths are detailed (2D array).
sanghob35a6192015-04-01 13:05:26 -0700202 *
Saurav Dasd2fded02016-12-02 15:43:47 -0800203 * @return the hash table of target Devices learned in multiple Dijkstra
Saurav Dasa07f2032015-10-19 14:37:36 -0700204 * iterations and corresponding ECMP paths in terms of Devices to
Saurav Dasd2fded02016-12-02 15:43:47 -0800205 * be traversed (via) from the root Device to the target Device
sanghob35a6192015-04-01 13:05:26 -0700206 */
207 public HashMap<Integer, HashMap<DeviceId,
208 ArrayList<ArrayList<DeviceId>>>> getAllLearnedSwitchesAndVia() {
209
Sho SHIMIZU6cfc02d2015-09-11 11:19:11 -0700210 HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> deviceViaMap = new HashMap<>();
sanghob35a6192015-04-01 13:05:26 -0700211
212 for (Integer itrIndx : distanceDeviceMap.keySet()) {
Sho SHIMIZU6cfc02d2015-09-11 11:19:11 -0700213 HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swMap = new HashMap<>();
sanghob35a6192015-04-01 13:05:26 -0700214
215 for (DeviceId sw : distanceDeviceMap.get(itrIndx)) {
216 ArrayList<ArrayList<DeviceId>> swViaArray = new ArrayList<>();
217 for (Path path : getECMPPaths(sw)) {
218 ArrayList<DeviceId> swVia = new ArrayList<>();
219 for (Link link : path.links()) {
220 if (link.src().deviceId().equals(rootDevice)) {
221 /* No need to add the root Device again in
222 * the Via list
223 */
224 continue;
225 }
226 swVia.add(link.src().deviceId());
227 }
228 swViaArray.add(swVia);
229 }
230 swMap.put(sw, swViaArray);
231 }
232 deviceViaMap.put(itrIndx, swMap);
233 }
234 return deviceViaMap;
235 }
236
237
238 private Link copyDefaultLink(Link link) {
239 DefaultLink src = (DefaultLink) link;
Ray Milkey2693bda2016-01-22 16:08:14 -0800240 DefaultLink defaultLink = DefaultLink.builder()
241 .providerId(src.providerId())
242 .src(src.src())
243 .dst(src.dst())
244 .type(src.type())
245 .annotations(src.annotations())
246 .build();
sanghob35a6192015-04-01 13:05:26 -0700247
248 return defaultLink;
249 }
250
251 @Override
252 public String toString() {
253 StringBuilder sBuilder = new StringBuilder();
254 for (Device device: srManager.deviceService.getDevices()) {
Saurav Das7bcbe702017-06-13 15:35:54 -0700255 if (!device.id().equals(rootDevice)) {
Saurav Dasc88d4662017-05-15 15:34:25 -0700256 sBuilder.append("\r\n Paths from " + rootDevice + " to "
257 + device.id());
sanghob35a6192015-04-01 13:05:26 -0700258 ArrayList<Path> paths = getECMPPaths(device.id());
259 if (paths != null) {
260 for (Path path : paths) {
Saurav Dasc88d4662017-05-15 15:34:25 -0700261 sBuilder.append("\r\n == "); // equal cost paths delimiter
262 for (int i = path.links().size() - 1; i >= 0; i--) {
263 Link link = path.links().get(i);
sanghob35a6192015-04-01 13:05:26 -0700264 sBuilder.append(" : " + link.src() + " -> " + link.dst());
265 }
266 }
Saurav Dasc88d4662017-05-15 15:34:25 -0700267 } else {
268 sBuilder.append("\r\n == no paths");
sanghob35a6192015-04-01 13:05:26 -0700269 }
270 }
271 }
272 return sBuilder.toString();
273 }
274}
275