multipoint decomposer + unit test improvements
[unimgr.git] / impl / src / main / java / org / opendaylight / unimgr / mef / nrp / impl / decomposer / DecompositionAction.java
index 5067ebc52b4908a40633e51424ccadf9e8b445ad..dc140accf7eaa876aab1e6dfe3b14eaedaf60c58 100644 (file)
@@ -8,18 +8,11 @@
 
 package org.opendaylight.unimgr.mef.nrp.impl.decomposer;
 
-import java.util.HashMap;
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
 import org.jgrapht.Graph;
 import org.jgrapht.GraphPath;
 import org.jgrapht.alg.shortestpath.DijkstraShortestPath;
+import org.jgrapht.graph.DefaultDirectedGraph;
 import org.jgrapht.graph.DefaultEdge;
-import org.jgrapht.graph.SimpleGraph;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
@@ -29,22 +22,28 @@ import org.opendaylight.unimgr.mef.nrp.api.Subrequrest;
 import org.opendaylight.unimgr.mef.nrp.api.TapiConstants;
 import org.opendaylight.unimgr.mef.nrp.common.NrpDao;
 import org.opendaylight.yang.gen.v1.urn.onf.params.xml.ns.yang.tapi.common.rev171113.OperationalState;
+import org.opendaylight.yang.gen.v1.urn.onf.params.xml.ns.yang.tapi.common.rev171113.PortDirection;
 import org.opendaylight.yang.gen.v1.urn.onf.params.xml.ns.yang.tapi.common.rev171113.Uuid;
 import org.opendaylight.yang.gen.v1.urn.onf.params.xml.ns.yang.tapi.topology.rev171113.topology.Node;
 import org.opendaylight.yang.gen.v1.urn.onf.params.xml.ns.yang.tapi.topology.rev171113.topology.context.Topology;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.*;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
 /**
  * @author bartosz.michalik@amartus.com
  */
-public class DecompositionAction {
+class DecompositionAction {
     private static final Logger LOG = LoggerFactory.getLogger(DecompositionAction.class);
     private final List<EndPoint> endpoints;
     private final DataBroker broker;
-    private HashMap<Uuid, Vertex> sipToNep = new HashMap<>();
+    private final HashMap<Uuid, Vertex> sipToNep = new HashMap<>();
 
-    public DecompositionAction(List<EndPoint> endpoints, DataBroker broker) {
+    DecompositionAction(List<EndPoint> endpoints, DataBroker broker) {
         Objects.requireNonNull(endpoints);
         Objects.requireNonNull(broker);
         if (endpoints.size() < 2) {
@@ -57,24 +56,47 @@ public class DecompositionAction {
     List<Subrequrest> decompose() throws FailureResult {
         Graph<Vertex, DefaultEdge> graph = prepareData();
 
-        List<Vertex> vertexes = endpoints.stream().map(e -> sipToNep.get(e.getEndpoint().getServiceInterfacePoint())).collect(Collectors.toList());
+        List<Vertex> vertices = endpoints.stream().map(e -> {
+            Vertex v = sipToNep.get(e.getEndpoint().getServiceInterfacePoint());
+            if((v.dir == PortDirection.OUTPUT && e.getEndpoint().getDirection() != PortDirection.OUTPUT) ||
+               (v.dir == PortDirection.INPUT && e.getEndpoint().getDirection() != PortDirection.INPUT)) {
+                throw new IllegalArgumentException("Port direction for " + e.getEndpoint().getLocalId() + " incompatible with NEP." +
+                        "CEP " + e.getEndpoint().getDirection() + "  NEP " + v.dir);
+            }
+            return new Vertex(v, e.getEndpoint().getDirection());
+        }).collect(Collectors.toList());
 
-        assert vertexes.size() > 1;
+        assert vertices.size() > 1;
 
-        if (vertexes.size() > 2) {
-            throw new IllegalStateException("currently only point to point is supported");
-        }
+        Set<GraphPath<Vertex, DefaultEdge>> paths = new HashSet<>();
+
+        Set<Vertex> inV = vertices.stream().filter(isInput).collect(Collectors.toSet());
+        Set<Vertex> outV = vertices.stream().filter(isOutput).collect(Collectors.toSet());
 
-        GraphPath<Vertex, DefaultEdge> path = DijkstraShortestPath.findPathBetween(graph, vertexes.get(0), vertexes.get(1));
+        //do the verification whether it is possible to connect two nodes.
+        inV.forEach(i ->
+                outV.stream().filter(o -> i != o).forEach(o -> {
+                    GraphPath<Vertex, DefaultEdge> path = DijkstraShortestPath.findPathBetween(graph, i, o);
+                    if(path != null) {
+                        LOG.debug("Couldn't find path between {} and  {}", i, o);
+                    }
+                    paths.add(path);
+                })
+        );
 
-        if (path == null) {
+        if(paths.stream().anyMatch(Objects::isNull)) {
+            LOG.info("At least single path between endpoints not found");
             return null;
         }
 
-        return path.getVertexList().stream().collect(Collectors.groupingBy(v -> v.getNodeUuid()))
-                .entrySet().stream().map(e -> {
-                    return new Subrequrest(e.getKey(), e.getValue().stream().map(v -> toEndPoint(v)).collect(Collectors.toList()));
+        List<Subrequrest> result = paths.stream()
+                .flatMap(gp -> gp.getVertexList().stream()).collect(Collectors.groupingBy(Vertex::getNodeUuid))
+                .entrySet().stream()
+                .map(e -> {
+                    Set<EndPoint> endpoints = e.getValue().stream().map(this::toEndPoint).collect(Collectors.toSet());
+                    return new Subrequrest(e.getKey(), new ArrayList<>(endpoints));
                 }).collect(Collectors.toList());
+        return result.isEmpty() ? null : result;
     }
 
     private EndPoint toEndPoint(Vertex v) {
@@ -84,20 +106,32 @@ public class DecompositionAction {
         return ep;
     }
 
-    private void connected(Graph<Vertex, DefaultEdge> graph, List<Vertex> vertices) {
-        for (int i = 0; i < vertices.size(); ++i) {
-            Vertex f = vertices.get(i);
-            //its OK if the vertex is added in internal loop nothing will happen
-            graph.addVertex(f);
-            for (int j = i + 1; j < vertices.size(); ++j) {
-                Vertex t = vertices.get(j);
-                graph.addVertex(t);
-                graph.addEdge(f,t);
-            }
-        }
+    private final Predicate<Vertex> isInput = v -> v.getDir() == PortDirection.BIDIRECTIONAL|| v.getDir() == PortDirection.INPUT;
+    private final Predicate<Vertex> isOutput = v -> v.getDir() == PortDirection.BIDIRECTIONAL || v.getDir() == PortDirection.OUTPUT;
+
+    private void interconnectNode(Graph<Vertex, DefaultEdge> graph, List<Vertex> vertices) {
+        vertices.forEach(graph::addVertex);
+        Set<Vertex> inV = vertices.stream().filter(isInput).collect(Collectors.toSet());
+        Set<Vertex> outV = vertices.stream().filter(isOutput).collect(Collectors.toSet());
+        interconnect(graph, inV, outV);
+    }
+
+    private void interconnectLink(Graph<Vertex, DefaultEdge> graph, List<Vertex> vertices) {
+        vertices.forEach(graph::addVertex);
+        Set<Vertex> inV = vertices.stream().filter(isInput).collect(Collectors.toSet());
+        Set<Vertex> outV = vertices.stream().filter(isOutput).collect(Collectors.toSet());
+        interconnect(graph, outV, inV);
+
+
     }
 
-    protected Graph<Vertex, DefaultEdge> prepareData() throws FailureResult {
+    private void interconnect(Graph<Vertex, DefaultEdge> graph, Collection<Vertex> from, Collection<Vertex> to) {
+        from.forEach(iV ->
+                to.stream().filter(oV -> iV != oV).forEach(oV -> graph.addEdge(iV,oV)));
+    }
+
+
+    private Graph<Vertex, DefaultEdge> prepareData() throws FailureResult {
         ReadWriteTransaction tx = broker.newReadWriteTransaction();
         try {
             Topology topo = new NrpDao(tx).getTopology(TapiConstants.PRESTO_SYSTEM_TOPO);
@@ -105,21 +139,23 @@ public class DecompositionAction {
                 throw new FailureResult("There are no nodes in {0} topology", TapiConstants.PRESTO_SYSTEM_TOPO);
             }
 
-            Graph<Vertex, DefaultEdge> graph = new SimpleGraph<>(DefaultEdge.class);
+            Graph<Vertex, DefaultEdge> graph = new DefaultDirectedGraph<>(DefaultEdge.class);
+
             topo.getNode().stream().map(this::nodeToGraph).forEach(vs -> {
                 List<Vertex> vertices = vs.collect(Collectors.toList());
                 vertices.forEach(v -> sipToNep.put(v.getSip(), v));
-                connected(graph, vertices);
+                interconnectNode(graph, vertices);
             });
 
             if (topo.getLink() != null) {
                 topo.getLink().stream()
                         .filter(l -> l.getState() != null && OperationalState.ENABLED == l.getState().getOperationalState())
                         .forEach(l -> {
+                            //we probably need to take link bidir/unidir into consideration as well
                     List<Vertex> vertices = l.getNodeEdgePoint().stream()
                             .map(nep -> graph.vertexSet().stream().filter(v -> v.getUuid().equals(nep)).findFirst())
                             .filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList());
-                    connected(graph, vertices);
+                    interconnectLink(graph, vertices);
                 });
             }
 
@@ -129,44 +165,57 @@ public class DecompositionAction {
         }
     }
 
-    protected Stream<Vertex> nodeToGraph(Node n) {
+    private Stream<Vertex> nodeToGraph(Node n) {
         Uuid nodeUuid = n.getUuid();
-        return n.getOwnedNodeEdgePoint().stream().map(nep -> {
+
+
+        return n.getOwnedNodeEdgePoint().stream()
+                .filter(ep -> ep.getLinkPortDirection() != null && ep.getLinkPortDirection() != PortDirection.UNIDENTIFIEDORUNKNOWN)
+                .map(nep -> {
             List<Uuid> sips = nep.getMappedServiceInterfacePoint();
             if (sips == null || sips.isEmpty()) {
-                return  new Vertex(nodeUuid, nep.getUuid(), null);
+                return  new Vertex(nodeUuid, nep.getUuid(), null, nep.getLinkPortDirection());
             }
             if (sips.size() > 1) {
                 LOG.warn("NodeEdgePoint {} have multiple ServiceInterfacePoint mapped, selecting first one", nep.getUuid());
             }
-            return new Vertex(nodeUuid, nep.getUuid(), sips.get(0));
+            return new Vertex(nodeUuid, nep.getUuid(), sips.get(0), nep.getLinkPortDirection());
 
         });
     }
 
-    public class Vertex implements Comparable<Vertex> {
+    class Vertex implements Comparable<Vertex> {
 
         private final Uuid nodeUuid;
         private final Uuid uuid;
         private final Uuid sip;
+        private final PortDirection dir;
+
+        Vertex(Vertex px, PortDirection csDir) {
+            this.nodeUuid = px.nodeUuid;
+            this.uuid = px.uuid;
+            this.sip = px.sip;
+            this.dir = csDir;
+        }
 
-        public Vertex(Uuid nodeUuid, Uuid uuid, Uuid sip) {
+        Vertex(Uuid nodeUuid, Uuid uuid, Uuid sip, PortDirection dir) {
             this.sip = sip;
+            this.dir = dir;
             Objects.requireNonNull(nodeUuid);
             Objects.requireNonNull(uuid);
             this.nodeUuid = nodeUuid;
             this.uuid = uuid;
         }
 
-        public Uuid getNodeUuid() {
+        Uuid getNodeUuid() {
             return nodeUuid;
         }
 
-        public Uuid getUuid() {
+        Uuid getUuid() {
             return uuid;
         }
 
-        public Uuid getSip() {
+        Uuid getSip() {
             return sip;
         }
 
@@ -182,6 +231,10 @@ public class DecompositionAction {
             return Objects.equals(uuid, vertex.uuid);
         }
 
+        PortDirection getDir() {
+            return dir;
+        }
+
         @Override
         public int hashCode() {
             return Objects.hash(uuid);