use of org.openkilda.pce.exception.UnroutableFlowException in project open-kilda by telstra.
the class BestWeightAndShortestPathFinder method findNPathsBetweenSwitches.
/**
* Find N (or less) best paths. To find N paths Yen's algorithm is used.
*
* @return an list of N (or less) best paths.
*/
private List<List<Edge>> findNPathsBetweenSwitches(AvailableNetwork network, SwitchId startSwitchId, SwitchId endSwitchId, int count, WeightFunction weightFunction, Function<Node, FindOneDirectionPathResult> getPath) throws UnroutableFlowException {
Node start = network.getSwitch(startSwitchId);
Node end = network.getSwitch(endSwitchId);
if (start == null || end == null) {
throw new UnroutableFlowException(format("Switch %s doesn't have links with enough bandwidth", start == null ? startSwitchId : endSwitchId));
}
// Determine the shortest path from the start to the end.
List<List<Edge>> bestPaths = new ArrayList<>();
List<Edge> firstPath = getPath.apply(start).getFoundPath();
if (firstPath.isEmpty()) {
return new ArrayList<>();
}
bestPaths.add(getPath.apply(start).getFoundPath());
// Initialize the set to store the potential kth shortest path.
// Use LinkedHashSet to have deterministic results.
Set<List<Edge>> potentialKthShortestPaths = new LinkedHashSet<>();
for (int k = 1; k < count; k++) {
List<Edge> bestPath = bestPaths.get(k - 1);
for (int i = 0; i < bestPath.size(); i++) {
// Spur node is retrieved from the previous k-shortest path.
Node spurNode = bestPath.get(i).getSrcSwitch();
// The sequence of edges from the start to the spur node (without spur node).
List<Edge> rootPath = new ArrayList<>(bestPath.subList(0, i));
Set<Edge> removedEdges = new HashSet<>();
// Remove the links that are part of the previous shortest paths which share the same root path.
for (List<Edge> path : bestPaths) {
if (path.size() > i && rootPath.equals(path.subList(0, i)) && spurNode.equals(path.get(i).getSrcSwitch())) {
removedEdges.add(path.get(i));
removeEdge(path.get(i));
}
}
for (Edge edge : rootPath) {
edge.getSrcSwitch().remove();
}
// Calculate the spur path from the spur node to the end.
List<Edge> pathFromSpurNode = getPath.apply(spurNode).getFoundPath();
if (!pathFromSpurNode.isEmpty()) {
List<Edge> totalPath = new ArrayList<>(rootPath);
// Entire path is made up of the root path and spur path.
totalPath.addAll(pathFromSpurNode);
// Add the potential k-shortest path to the heap.
potentialKthShortestPaths.add(totalPath);
}
// Add back the edges and nodes that were removed from the graph.
for (Edge edge : removedEdges) {
restoreEdge(edge);
}
for (Edge edge : rootPath) {
edge.getSrcSwitch().restore();
}
}
if (potentialKthShortestPaths.isEmpty()) {
break;
}
// Add the lowest weight path becomes the k-shortest path.
List<Edge> newBestPath = getBestPotentialKthShortestPath(potentialKthShortestPaths, bestPaths, weightFunction);
bestPaths.add(newBestPath);
}
return bestPaths;
}
use of org.openkilda.pce.exception.UnroutableFlowException in project open-kilda by telstra.
the class AllocateProtectedResourcesAction method allocate.
@Override
protected void allocate(FlowUpdateFsm stateMachine) throws RecoverableException, UnroutableFlowException, ResourceAllocationException {
String flowId = stateMachine.getFlowId();
Set<String> flowIds = Sets.newHashSet(flowId);
if (stateMachine.getBulkUpdateFlowIds() != null) {
flowIds.addAll(stateMachine.getBulkUpdateFlowIds());
}
log.debug("Finding protected path ids for flows {}", flowIds);
List<PathId> pathIdsToReuse = new ArrayList<>(flowPathRepository.findActualPathIdsByFlowIds(flowIds));
pathIdsToReuse.addAll(stateMachine.getRejectedPaths());
Flow tmpFlow = getFlow(flowId);
FlowPathPair oldPaths = new FlowPathPair(tmpFlow.getProtectedForwardPath(), tmpFlow.getProtectedReversePath());
FlowPath primaryForward = getFlowPath(tmpFlow, stateMachine.getNewPrimaryForwardPath());
FlowPath primaryReverse = getFlowPath(tmpFlow, stateMachine.getNewPrimaryReversePath());
Predicate<GetPathsResult> testNonOverlappingPath = path -> (primaryForward == null || !flowPathBuilder.arePathsOverlapped(path.getForward(), primaryForward)) && (primaryReverse == null || !flowPathBuilder.arePathsOverlapped(path.getReverse(), primaryReverse));
PathId newForwardPathId = resourcesManager.generatePathId(flowId);
PathId newReversePathId = resourcesManager.generatePathId(flowId);
log.debug("Finding a new protected path for flow {}", flowId);
GetPathsResult allocatedPaths = allocatePathPair(tmpFlow, newForwardPathId, newReversePathId, false, pathIdsToReuse, oldPaths, true, stateMachine.getSharedBandwidthGroupId(), testNonOverlappingPath);
if (allocatedPaths == null) {
throw new ResourceAllocationException("Unable to allocate a path");
}
if (!testNonOverlappingPath.test(allocatedPaths)) {
stateMachine.saveActionToHistory("Couldn't find non overlapping protected path");
} else {
log.debug("New protected paths have been allocated: {}", allocatedPaths);
stateMachine.setNewProtectedForwardPath(newForwardPathId);
stateMachine.setNewProtectedReversePath(newReversePathId);
stateMachine.setBackUpProtectedPathComputationWayUsed(allocatedPaths.isBackUpPathComputationWayUsed());
log.debug("Allocating resources for a new protected path of flow {}", flowId);
FlowResources flowResources = allocateFlowResources(tmpFlow, newForwardPathId, newReversePathId);
stateMachine.setNewProtectedResources(flowResources);
FlowPathPair createdPaths = createFlowPathPair(flowId, flowResources, allocatedPaths, false, stateMachine.getSharedBandwidthGroupId());
log.debug("New protected path has been created: {}", createdPaths);
saveAllocationActionWithDumpsToHistory(stateMachine, tmpFlow, "protected", createdPaths);
}
}
use of org.openkilda.pce.exception.UnroutableFlowException in project open-kilda by telstra.
the class InMemoryPathComputer method getNPaths.
@Override
public List<Path> getNPaths(SwitchId srcSwitchId, SwitchId dstSwitchId, int count, FlowEncapsulationType flowEncapsulationType, PathComputationStrategy pathComputationStrategy, Duration maxLatency, Duration maxLatencyTier2) throws RecoverableException, UnroutableFlowException {
final long maxLatencyNs = maxLatency != null ? maxLatency.toNanos() : 0;
final long maxLatencyTier2Ns = maxLatencyTier2 != null ? maxLatencyTier2.toNanos() : 0;
Flow flow = Flow.builder().flowId(// just any id, as not used.
"").srcSwitch(Switch.builder().switchId(srcSwitchId).build()).destSwitch(Switch.builder().switchId(dstSwitchId).build()).ignoreBandwidth(false).encapsulationType(flowEncapsulationType).bandwidth(// to get ISLs with non zero available bandwidth
1).maxLatency(maxLatencyNs).maxLatencyTier2(maxLatencyTier2Ns).build();
AvailableNetwork availableNetwork = availableNetworkFactory.getAvailableNetwork(flow, Collections.emptyList());
if (MAX_LATENCY.equals(pathComputationStrategy) && (flow.getMaxLatency() == null || flow.getMaxLatency() == 0)) {
pathComputationStrategy = LATENCY;
}
List<List<Edge>> paths;
switch(pathComputationStrategy) {
case COST:
case LATENCY:
case COST_AND_AVAILABLE_BANDWIDTH:
paths = pathFinder.findNPathsBetweenSwitches(availableNetwork, srcSwitchId, dstSwitchId, count, getWeightFunctionByStrategy(pathComputationStrategy));
break;
case MAX_LATENCY:
paths = pathFinder.findNPathsBetweenSwitches(availableNetwork, srcSwitchId, dstSwitchId, count, getWeightFunctionByStrategy(pathComputationStrategy), maxLatencyNs, maxLatencyTier2Ns);
break;
default:
throw new UnsupportedOperationException(String.format("Unsupported strategy type %s", pathComputationStrategy));
}
Comparator<Path> comparator;
if (pathComputationStrategy == LATENCY || pathComputationStrategy == MAX_LATENCY) {
comparator = Comparator.comparing(Path::getLatency).thenComparing(Comparator.comparing(Path::getMinAvailableBandwidth).reversed());
} else {
comparator = Comparator.comparing(Path::getMinAvailableBandwidth).reversed().thenComparing(Path::getLatency);
}
return paths.stream().map(edges -> convertToPath(srcSwitchId, dstSwitchId, edges)).sorted(comparator).limit(count).collect(Collectors.toList());
}
use of org.openkilda.pce.exception.UnroutableFlowException in project open-kilda by telstra.
the class BestWeightAndShortestPathFinderTest method shouldFindSymmetricPath.
@Test
public void shouldFindSymmetricPath() throws UnroutableFlowException {
AvailableNetwork network = buildLinearNetworkWithPairLinks();
BestWeightAndShortestPathFinder pathFinder = new BestWeightAndShortestPathFinder(2);
Pair<List<Edge>, List<Edge>> pathPair = pathFinder.findPathWithMinWeight(network, SWITCH_ID_1, SWITCH_ID_3, WEIGHT_FUNCTION).getFoundPath();
List<Edge> forward = pathPair.getLeft();
List<Edge> reverse = Lists.reverse(pathPair.getRight());
List<Boolean> validation = IntStream.range(0, forward.size()).mapToObj(i -> Objects.equals(forward.get(i).getSrcPort(), reverse.get(i).getDestPort())).collect(Collectors.toList());
assertFalse(validation.contains(false));
}
use of org.openkilda.pce.exception.UnroutableFlowException in project open-kilda by telstra.
the class ResourcesAllocationAction method allocateProtectedPath.
private void allocateProtectedPath(FlowCreateFsm stateMachine) throws UnroutableFlowException, RecoverableException, ResourceAllocationException, FlowNotFoundException {
String flowId = stateMachine.getFlowId();
Flow tmpFlow = getFlow(flowId);
if (!tmpFlow.isAllocateProtectedPath()) {
return;
}
tmpFlow.setDiverseGroupId(getFlowDiverseGroupFromContext(flowId).orElseThrow(() -> new FlowNotFoundException(flowId)));
GetPathsResult protectedPath = pathComputer.getPath(tmpFlow);
stateMachine.setBackUpProtectedPathComputationWayUsed(protectedPath.isBackUpPathComputationWayUsed());
boolean overlappingProtectedPathFound = flowPathBuilder.arePathsOverlapped(protectedPath.getForward(), tmpFlow.getForwardPath()) || flowPathBuilder.arePathsOverlapped(protectedPath.getReverse(), tmpFlow.getReversePath());
if (overlappingProtectedPathFound) {
log.info("Couldn't find non overlapping protected path. Result flow state: {}", tmpFlow);
throw new UnroutableFlowException("Couldn't find non overlapping protected path", tmpFlow.getFlowId());
}
log.debug("Creating the protected path {} for flow {}", protectedPath, tmpFlow);
transactionManager.doInTransaction(() -> {
Flow flow = getFlow(flowId);
FlowResources flowResources = resourcesManager.allocateFlowResources(flow);
final FlowSegmentCookieBuilder cookieBuilder = FlowSegmentCookie.builder().flowEffectiveId(flowResources.getUnmaskedCookie());
FlowPath forward = flowPathBuilder.buildFlowPath(flow, flowResources.getForward(), protectedPath.getForward(), cookieBuilder.direction(FlowPathDirection.FORWARD).build(), false, stateMachine.getSharedBandwidthGroupId());
forward.setStatus(FlowPathStatus.IN_PROGRESS);
flowPathRepository.add(forward);
flow.setProtectedForwardPath(forward);
FlowPath reverse = flowPathBuilder.buildFlowPath(flow, flowResources.getReverse(), protectedPath.getReverse(), cookieBuilder.direction(FlowPathDirection.REVERSE).build(), false, stateMachine.getSharedBandwidthGroupId());
reverse.setStatus(FlowPathStatus.IN_PROGRESS);
flowPathRepository.add(reverse);
flow.setProtectedReversePath(reverse);
updateIslsForFlowPath(forward.getPathId());
updateIslsForFlowPath(reverse.getPathId());
stateMachine.setProtectedForwardPathId(forward.getPathId());
stateMachine.setProtectedReversePathId(reverse.getPathId());
log.debug("Allocated resources for the flow {}: {}", flow.getFlowId(), flowResources);
stateMachine.getFlowResources().add(flowResources);
});
}
Aggregations