Open Kilda Java Documentation
SimpleGetShortestPath.java
Go to the documentation of this file.
1 /* Copyright 2018 Telstra Open Source
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 package org.openkilda.pce.algo;
17 
22 
23 import com.google.common.collect.Lists;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26 
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.HashMap;
30 import java.util.LinkedList;
31 import java.util.List;
32 import java.util.Set;
33 
59 public class SimpleGetShortestPath {
60 
61  private static final Logger logger = LoggerFactory.getLogger(SimpleGetShortestPath.class);
62 
63  private AvailableNetwork network;
64  private SimpleSwitch start;
65  private SimpleSwitch end;
66  private int allowedDepth;
67  private int bestCost = Integer.MAX_VALUE;
68  private SearchNode bestPath = null;
69 
70  public SimpleGetShortestPath(AvailableNetwork network, SwitchId srcDpid, SwitchId dstDpid, int allowedDepth) {
71  this.network = network;
72  this.start = network.getSwitches().get(srcDpid);
73  this.end = network.getSwitches().get(dstDpid);
74  this.allowedDepth = allowedDepth;
75  if (this.start == null) {
76  logger.warn("SOURCE node doesn't exist. It isn't in the AVAILABLE network: {}", srcDpid);
77  }
78  if (this.end == null) {
79  logger.warn("DESTINATION node doesn't exist. It isn't in the AVAILABLE network: {}", dstDpid);
80  }
81  }
82 
83 
90  public LinkedList<SimpleIsl> getPath() {
91  HashMap<SimpleSwitch, SearchNode> toVisitLookup = new HashMap<>(); // constant lookup
92  LinkedList<SearchNode> toVisit = new LinkedList<>(); // working list
93  HashMap<SimpleSwitch, SearchNode> visited = new HashMap<>();
94 
95  if (start != null && end != null) {
96  toVisitLookup.put(start, new SearchNode(allowedDepth, 0, start));
97  toVisit.add(toVisitLookup.get(start));
98  }
99 
100  while (toVisit.size() > 0) {
101  SearchNode current = toVisit.pop();
102 
103  // Determine if this node is the destination node.
104  if (current.dstSw.equals(end)) {
105  // We found the destination
106  if (current.parentCost < bestCost) {
107  // We found a best path. If we don't get here, then the entire graph will be
108  // searched until we run out of nodes or the depth is reached.
109  bestCost = current.parentCost;
110  bestPath = current;
111  }
112  // We found dest, no need to keep processing
113  continue;
114  }
115 
116  // Otherwise, if we've been here before, see if this path is better
117  SearchNode prior = visited.get(current.dstSw);
118  if (prior != null) {
119  // We've already visited this one .. keep going only if this is cheaper
120  if (current.parentCost >= prior.parentCost) {
121  continue;
122  }
123  }
124  // Either this is the first time, or this is cheaper .. either way, this node should
125  // be the one in the visited list
126  visited.put(current.dstSw, current);
127 
128  // Stop processing entirely if we've gone too far, or over bestCost
129  if (current.allowedDepth < 0 || current.parentCost > bestCost) {
130  continue;
131  }
132 
133  // At this stage .. haven't found END, haven't gone too deep, and we are not over cost.
134  // So, add the outbound isls.
135  for (Set<SimpleIsl> isls: current.dstSw.outbound.values()) {
136  for (SimpleIsl isl : isls) {
137  toVisit.add(current.addNode(isl));
138  }
139  }
140  }
141 
142  return (bestPath != null) ? bestPath.parentPath : new LinkedList<>();
143  }
144 
145 
159  public LinkedList<SimpleIsl> getPath(List<SimpleIsl> hint) {
160  // First, see if the first and last nodes match our start and end, or whether the list
161  // needs to be reversed
162  if (hint != null && hint.size() > 0) {
163  SimpleSwitch from = network.getSimpleSwitch(hint.get(0).getSrcDpid());
164  SimpleSwitch to = network.getSimpleSwitch(hint.get(hint.size() - 1).getDstDpid());
165  if (start.equals(to) && end.equals(from)) {
166  logger.trace("getPath w/ Hint: Reversing the path from {}->{} to {}->{}", from, to, to, from);
167  // looks like hint wasn't reversed .. revers the order, and swap src/dst.
168  hint = swapSrcDst(Lists.reverse(hint));
169  // re-run from and to .. it should now match.
170  from = network.getSimpleSwitch(hint.get(0).getSrcDpid());
171  to = network.getSimpleSwitch(hint.get(hint.size() - 1).getDstDpid());
172  }
173 
174  // If the start/end equals from/to, then confirm the path and see if we can use it.
175  if (start.equals(from) && end.equals(to)) {
176  logger.trace("getPath w/ Hint: hint matches start/ed {}->{}", from, to);
177  // need to validate that the path exists .. and if so .. set bestCost and bestPath
178  SearchNode best = confirmIsls(hint);
179  if (best != null) {
180  logger.debug("getPath w/ Hint: the hint path EXISTS for {}->{}", from, to);
181  bestCost = best.parentCost;
182  bestPath = best;
183  } else {
184  logger.info("getPath w/ Hint: the hint path DOES NOT EXIST for {}->{}, will find new path",
185  from, to);
186  }
187  }
188  }
189  return getPath();
190  }
191 
195  private List<SimpleIsl> swapSrcDst(List<SimpleIsl> originalIsls) {
196  List<SimpleIsl> mirrorIsls = new ArrayList<>();
197  for (SimpleIsl original : originalIsls) {
198  // this swaps the src and dst fields
199  mirrorIsls.add(new SimpleIsl(original.getDstDpid(), original.getSrcDpid(), original.getDstPort(),
200  original.getSrcPort(), original.getCost(), original.getLatency()));
201  }
202  return mirrorIsls;
203  }
204 
211  private static final Set<SimpleIsl> safeSet(Set<SimpleIsl> other) {
212  return other == null ? Collections.EMPTY_SET : other;
213  }
214 
218  private SearchNode confirmIsls(List<SimpleIsl> srcIsls) {
219  int totalCost = 0;
220  LinkedList<SimpleIsl> confirmedIsls = new LinkedList<>();
221 
222  boolean validPath = true;
223  for (SimpleIsl i : srcIsls) {
224  boolean foundThisOne = false;
225  SimpleSwitch srcSwitch = network.getSimpleSwitch(i.getSrcDpid());
226  if (srcSwitch != null) {
227  Set<SimpleIsl> pathsToDst = safeSet(srcSwitch.outbound.get(i.getDstDpid()));
228  if (pathsToDst.equals(Collections.EMPTY_SET)) {
229  logger.debug("No ISLS from {} to {}", i.getSrcDpid(), i.getDstDpid());
230  }
231  for (SimpleIsl orig : pathsToDst) {
232  if (i.equals(orig)) {
233  foundThisOne = true;
234  confirmedIsls.add(orig);
235  totalCost += orig.getCost();
236  break; // stop looking, we found the isl
237  }
238  }
239  } else {
240  logger.warn("confirmIsls: Found a null switch during getPath(hint): {}", i.getSrcDpid());
241  }
242  if (!foundThisOne) {
243  validPath = false;
244  break; // found an isl that doesn't exist, stop looking for others
245  }
246  }
247 
248  if (validPath) {
249  return new SearchNode(this.allowedDepth - confirmedIsls.size(), totalCost,
250  network.getSimpleSwitch(confirmedIsls.peekLast().getDstDpid()), confirmedIsls);
251  }
252  return null;
253  }
254 
255 
260  private class SearchNode implements Cloneable {
261  SimpleSwitch dstSw;
262  int allowedDepth;
263  int parentCost;
264  LinkedList<SimpleIsl> parentPath;
265  // NB: We could consider tracking the child path as well .. to facilitate
266  // re-calc if we find a better parentPath.
267 
268  public SearchNode(int allowedDepth, int parentCost, SimpleSwitch dstSw) {
269  this(allowedDepth, parentCost, dstSw, new LinkedList<SimpleIsl>());
270  }
271 
272  public SearchNode(int allowedDepth, int parentCost, SimpleSwitch dstSw, LinkedList<SimpleIsl> parentPath) {
273  this.dstSw = dstSw;
274  this.allowedDepth = allowedDepth;
275  this.parentCost = parentCost;
276  this.parentPath = parentPath;
277  }
278 
279  public SearchNode addNode(SimpleIsl nextIsl) {
280  SearchNode newNode = this.clone();
281  newNode.parentPath.add(nextIsl);
282  newNode.dstSw = network.getSimpleSwitch(nextIsl.getDstDpid());
283  newNode.allowedDepth--;
284  newNode.parentCost += nextIsl.getCost();
285  return newNode;
286  }
287 
288  @Override
289  @SuppressWarnings("unchecked")
290  protected SearchNode clone() {
291  return new SearchNode(allowedDepth, parentCost, dstSw, (LinkedList<SimpleIsl>) parentPath.clone());
292  }
293  }
294 
295  private void createSearchNode() {
296 
297  }
298 }
Map< SwitchId, SimpleSwitch > getSwitches()
LinkedList< SimpleIsl > getPath(List< SimpleIsl > hint)
SimpleGetShortestPath(AvailableNetwork network, SwitchId srcDpid, SwitchId dstDpid, int allowedDepth)
SimpleSwitch getSimpleSwitch(SwitchId dpid)