2 * Copyright (c) 2017 Cisco Systems Inc and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.unimgr.mef.nrp.impl.decomposer;
11 import java.util.ArrayList;
12 import java.util.Collection;
13 import java.util.Collections;
14 import java.util.HashMap;
15 import java.util.HashSet;
16 import java.util.List;
17 import java.util.Objects;
18 import java.util.Optional;
20 import java.util.function.Function;
21 import java.util.function.Predicate;
22 import java.util.stream.Collectors;
23 import java.util.stream.Stream;
25 import org.jgrapht.Graph;
26 import org.jgrapht.GraphPath;
27 import org.jgrapht.alg.shortestpath.DijkstraShortestPath;
28 import org.jgrapht.graph.DefaultDirectedGraph;
29 import org.jgrapht.graph.DefaultEdge;
30 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
31 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
32 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
33 import org.opendaylight.unimgr.mef.nrp.api.EndPoint;
34 import org.opendaylight.unimgr.mef.nrp.api.FailureResult;
35 import org.opendaylight.unimgr.mef.nrp.api.Subrequrest;
36 import org.opendaylight.unimgr.mef.nrp.api.TapiConstants;
37 import org.opendaylight.unimgr.mef.nrp.common.NrpDao;
38 import org.opendaylight.unimgr.mef.nrp.common.TapiUtils;
39 import org.opendaylight.yang.gen.v1.urn.odl.unimgr.yang.unimgr.ext.rev170531.NodeAdiAugmentation;
40 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev180307.OperationalState;
41 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev180307.PortDirection;
42 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev180307.ServiceInterfacePointRef;
43 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev180307.Uuid;
44 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev180307.connectivity.service.end.point.ServiceInterfacePoint;
45 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.topology.rev180307.topology.Node;
46 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.topology.rev180307.topology.context.Topology;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
52 * Decompose request into sub-requests {@link Subrequrest} for drivers using graph path computation.
53 * @author bartosz.michalik@amartus.com
55 class DecompositionAction {
56 private static final Logger LOG = LoggerFactory.getLogger(DecompositionAction.class);
57 private final List<EndPoint> endpoints;
58 private final DataBroker broker;
59 private final HashMap<Uuid, Vertex> sipToNep = new HashMap<>();
61 DecompositionAction(List<EndPoint> endpoints, DataBroker broker) {
62 Objects.requireNonNull(endpoints);
63 Objects.requireNonNull(broker);
64 if (endpoints.size() < 2) {
65 throw new IllegalArgumentException("there should be at least two endpoints defined");
67 this.endpoints = endpoints;
71 Function<ServiceInterfacePoint, Uuid> toUuid = s -> s == null ? null : s.getServiceInterfacePointId();
73 List<Subrequrest> decompose() throws FailureResult {
74 Graph<Vertex, DefaultEdge> graph = prepareData();
78 Set<String> missingSips = endpoints.stream()
79 .filter(e -> sipToNep.get(toUuid.apply(e.getEndpoint().getServiceInterfacePoint())) == null)
80 .map(e -> toUuid.apply(e.getEndpoint().getServiceInterfacePoint()).getValue())
81 .collect(Collectors.toSet());
82 if (!missingSips.isEmpty()) {
83 throw new FailureResult("Some service interface points not found in the system: "
84 + missingSips.stream().collect(Collectors.joining(",", "[", "]")));
87 List<Vertex> vertices = endpoints.stream().map(e -> {
88 Vertex vertex = sipToNep.get(toUuid.apply(e.getEndpoint().getServiceInterfacePoint()));
89 if ((vertex.dir == PortDirection.OUTPUT && e.getEndpoint().getDirection() != PortDirection.OUTPUT)
90 || (vertex.dir == PortDirection.INPUT && e.getEndpoint().getDirection() != PortDirection.INPUT)) {
91 throw new IllegalArgumentException("Port direction for "
92 + e.getEndpoint().getLocalId() + " incompatible with NEP."
93 + "CEP " + e.getEndpoint().getDirection() + " NEP " + vertex.dir);
95 return new Vertex(vertex, e.getEndpoint().getDirection());
96 }).collect(Collectors.toList());
98 assert vertices.size() > 1;
100 Set<GraphPath<Vertex, DefaultEdge>> paths = new HashSet<>();
102 Set<Vertex> inV = vertices.stream().filter(isInput).collect(Collectors.toSet());
103 Set<Vertex> outV = vertices.stream().filter(isOutput).collect(Collectors.toSet());
105 //do the verification whether it is possible to connect two nodes.
107 outV.stream().filter(o -> i != o).forEach(o -> {
108 GraphPath<Vertex, DefaultEdge> path = DijkstraShortestPath.findPathBetween(graph, i, o);
110 LOG.debug("Couldn't find path between {} and {}", i, o);
116 if (paths.stream().anyMatch(Objects::isNull)) {
117 LOG.info("At least single path between endpoints not found");
121 List<Subrequrest> result = toSublists(paths);
122 return result.isEmpty() ? null : result;
125 private List<Subrequrest> toSublists(Set<GraphPath<Vertex, DefaultEdge>> paths) {
126 return paths.stream()
127 .flatMap(gp -> gp.getVertexList().stream()).collect(Collectors.groupingBy(Vertex::getNodeUuid))
130 Set<EndPoint> fromVertexes = e.getValue().stream()
131 .map(this::toEndPoint).collect(Collectors.toSet());
132 return new Subrequrest(
134 new ArrayList<>(fromVertexes),
135 e.getValue().stream().findFirst().get().getActivationDriverId());
136 }).collect(Collectors.toList());
139 private EndPoint toEndPoint(Vertex vertex) {
140 EndPoint ep = endpoints.stream()
141 .filter(e -> e.getEndpoint().getServiceInterfacePoint()
142 .getServiceInterfacePointId().equals(vertex.getSip()))
144 .orElse(new EndPoint(null, null));
145 Objects.requireNonNull(vertex.getUuid());
146 ep.setNepRef(TapiUtils.toSysNepRef(vertex.getNodeUuid(), vertex.getUuid()));
150 private final Predicate<Vertex> isInput = v -> v.getDir() == PortDirection.BIDIRECTIONAL
151 || v.getDir() == PortDirection.INPUT;
152 private final Predicate<Vertex> isOutput = v -> v.getDir() == PortDirection.BIDIRECTIONAL
153 || v.getDir() == PortDirection.OUTPUT;
155 private void interconnectNode(Graph<Vertex, DefaultEdge> graph, List<Vertex> vertices) {
156 vertices.forEach(graph::addVertex);
157 Set<Vertex> inV = vertices.stream().filter(isInput).collect(Collectors.toSet());
158 Set<Vertex> outV = vertices.stream().filter(isOutput).collect(Collectors.toSet());
159 interconnect(graph, inV, outV);
162 private void interconnectLink(Graph<Vertex, DefaultEdge> graph, List<Vertex> vertices) {
163 vertices.forEach(graph::addVertex);
164 Set<Vertex> inV = vertices.stream().filter(isInput).collect(Collectors.toSet());
165 Set<Vertex> outV = vertices.stream().filter(isOutput).collect(Collectors.toSet());
166 interconnect(graph, outV, inV);
171 private void interconnect(Graph<Vertex, DefaultEdge> graph, Collection<Vertex> from, Collection<Vertex> to) {
173 to.stream().filter(oV -> iV != oV).forEach(oV -> graph.addEdge(iV,oV)));
177 private Graph<Vertex, DefaultEdge> prepareData() throws FailureResult {
178 ReadWriteTransaction tx = broker.newReadWriteTransaction();
180 Topology topo = new NrpDao(tx).getTopology(TapiConstants.PRESTO_SYSTEM_TOPO);
181 if (topo.getNode() == null) {
182 throw new FailureResult("There are no nodes in {0} topology", TapiConstants.PRESTO_SYSTEM_TOPO);
185 Graph<Vertex, DefaultEdge> graph = new DefaultDirectedGraph<>(DefaultEdge.class);
187 topo.getNode().stream().map(this::nodeToGraph).forEach(vs -> {
188 List<Vertex> vertices = vs.collect(Collectors.toList());
189 vertices.forEach(v -> sipToNep.put(v.getSip(), v));
190 interconnectNode(graph, vertices);
193 if (topo.getLink() != null) {
194 topo.getLink().stream()
195 .filter(l -> OperationalState.ENABLED == l.getOperationalState())
197 //we probably need to take link bidir/unidir into consideration as well
198 List<Vertex> vertices = l.getNodeEdgePoint().stream()
199 .map(nep -> graph.vertexSet().stream()
200 .filter(v -> v.getUuid().equals(nep.getOwnedNodeEdgePointId())).findFirst())
201 .filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList());
202 interconnectLink(graph, vertices);
207 } catch (ReadFailedException e) {
208 throw new FailureResult("Cannot read {0} topology", TapiConstants.PRESTO_SYSTEM_TOPO);
212 private Stream<Vertex> nodeToGraph(Node node) {
213 Uuid nodeUuid = node.getUuid();
214 String activationDriverId = node.augmentation(NodeAdiAugmentation.class).getActivationDriverId();
217 return node.getOwnedNodeEdgePoint().stream()
218 .filter(ep -> ep.getLinkPortDirection() != null
219 && ep.getLinkPortDirection() != PortDirection.UNIDENTIFIEDORUNKNOWN)
221 List<Uuid> sips = Collections.emptyList();
222 if (nep.getMappedServiceInterfacePoint() != null) {
223 sips = nep.getMappedServiceInterfacePoint().stream()
224 .map(ServiceInterfacePointRef::getServiceInterfacePointId)
225 .collect(Collectors.toList());
228 if (sips.isEmpty()) {
229 return new Vertex(nodeUuid, nep.getUuid(), null, nep.getLinkPortDirection(),activationDriverId);
231 if (sips.size() > 1) {
232 LOG.warn("NodeEdgePoint {} have multiple ServiceInterfacePoint mapped, selecting first one",
235 return new Vertex(nodeUuid, nep.getUuid(), sips.get(0), nep.getLinkPortDirection(),activationDriverId);
240 class Vertex implements Comparable<Vertex> {
242 private final Uuid nodeUuid;
243 private final Uuid uuid;
244 private final Uuid sip;
245 private final String activationDriverId;
246 private final PortDirection dir;
248 Vertex(Vertex px, PortDirection csDir) {
249 this.nodeUuid = px.nodeUuid;
253 this.activationDriverId = px.activationDriverId;
256 Vertex(Uuid nodeUuid, Uuid uuid, Uuid sip, PortDirection dir, String activationDriverId) {
259 Objects.requireNonNull(nodeUuid);
260 Objects.requireNonNull(uuid);
261 Objects.requireNonNull(activationDriverId);
263 this.nodeUuid = nodeUuid;
265 this.activationDriverId = activationDriverId;
280 public String getActivationDriverId() {
281 return activationDriverId;
285 public boolean equals(Object other) {
289 if (other == null || getClass() != other.getClass()) {
292 Vertex vertex = (Vertex) other;
293 return Objects.equals(uuid, vertex.uuid);
296 PortDirection getDir() {
301 public int hashCode() {
302 return Objects.hash(uuid);
306 public int compareTo(Vertex other) {
310 return uuid.getValue().compareTo(other.uuid.getValue());
314 public String toString() {
315 return "V{" + uuid.getValue() + '}';