80c1cc2d070551043fa742894ba94b4157649b01
[transportpce.git] / pce / src / main / java / org / opendaylight / transportpce / pce / PceCalculation.java
1 /*
2  * Copyright © 2017 AT&T, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.transportpce.pce;
9
10 import com.google.common.base.Optional;
11
12 import java.util.ArrayList;
13 import java.util.HashMap;
14 import java.util.HashSet;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.concurrent.ExecutionException;
18 import java.util.stream.Collectors;
19
20 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
21 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
22 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
23 import org.opendaylight.transportpce.common.NetworkUtils;
24 import org.opendaylight.transportpce.common.ResponseCodes;
25 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev190624.PathComputationRequestInput;
26 import org.opendaylight.yang.gen.v1.http.org.openroadm.network.topology.rev181130.Node1;
27 import org.opendaylight.yang.gen.v1.http.org.openroadm.network.types.rev181130.OpenroadmNodeType;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.rev180226.NetworkId;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.rev180226.Networks;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.rev180226.NodeId;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.rev180226.networks.Network;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.rev180226.networks.NetworkKey;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.rev180226.networks.network.Node;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.topology.rev180226.LinkId;
35 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.topology.rev180226.Network1;
36 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.topology.rev180226.networks.network.Link;
37 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 public class PceCalculation {
42     /* Logging. */
43     private static final Logger LOG = LoggerFactory.getLogger(PceCalculation.class);
44     private DataBroker dataBroker = null;
45     ///////////// data parsed from Input/////////////////
46     private PathComputationRequestInput input;
47     private String anodeId = "";
48     private String znodeId = "";
49     private PceConstraints pceHardConstraints;
50     private PceConstraints pceSoftConstraints;
51     ///////////// Intermediate data/////////////////
52     private List<PceLink> addLinks = new ArrayList<PceLink>();
53     private List<PceLink> dropLinks = new ArrayList<PceLink>();
54     private HashSet<NodeId> azSrgs = new HashSet<NodeId>();
55     private PceNode aendPceNode = null;
56     private PceNode zendPceNode = null;
57     private List<Link> allLinks = null;
58     private List<Node> allNodes = null;
59     // this List serves graph calculation
60     private Map<NodeId, PceNode> allPceNodes = new HashMap<NodeId, PceNode>();
61     // this List serves calculation of ZtoA path descritopn
62     // TODO maybe better solution is possible
63     private Map<LinkId, PceLink> allPceLinks = new HashMap<LinkId, PceLink>();
64     private PceResult returnStructure;
65
66     public PceResult getReturnStructure() {
67         return this.returnStructure;
68     }
69
70     public enum NodeConstraint {
71         NONE, HARD_EXCLUDE, HARD_INCLUDE, HARD_DIVERSITY, SOFT_EXCLUDE, SOFT_INCLUDE, SOFT_DIVERSITY;
72     }
73
74     public PceCalculation(PathComputationRequestInput input, DataBroker dataBroker, PceConstraints pceHardConstraints,
75             PceConstraints pceSoftConstraints, PceResult rc) {
76         this.input = input;
77         this.dataBroker = dataBroker;
78         this.returnStructure = rc;
79         this.pceHardConstraints = pceHardConstraints;
80         this.pceSoftConstraints = pceSoftConstraints;
81         parseInput();
82     }
83
84     // apply constraints to get applicable result
85     public void calcPath() {
86         LOG.info("In PceCalculation calcPath: ");
87         if (!readMdSal()) {
88             this.returnStructure.setRC(ResponseCodes.RESPONSE_FAILED);
89             return;
90         }
91         if (!analyzeNw()) {
92             this.returnStructure.setRC(ResponseCodes.RESPONSE_FAILED);
93             return;
94         }
95         this.returnStructure.setRC(ResponseCodes.RESPONSE_OK);
96         return;
97     }
98
99     private boolean parseInput() {
100         this.anodeId = this.input.getServiceAEnd().getNodeId();
101         this.znodeId = this.input.getServiceZEnd().getNodeId();
102         LOG.info("parseInput: A and Z :[{}] and [{}]", this.anodeId, this.znodeId);
103         this.returnStructure.setRate(this.input.getServiceAEnd().getServiceRate());
104         return true;
105     }
106
107     private boolean readMdSal() {
108         LOG.info("readMdSal: network {}", NetworkUtils.OVERLAY_NETWORK_ID);
109         InstanceIdentifier<Network> nwInstanceIdentifier = InstanceIdentifier.builder(Networks.class)
110             .child(Network.class, new NetworkKey(new NetworkId(NetworkUtils.OVERLAY_NETWORK_ID))).build();
111         ReadOnlyTransaction readOnlyTransaction = this.dataBroker.newReadOnlyTransaction();
112         Network nw = null;
113         try {
114             Optional<Network> nwOptional =
115                     readOnlyTransaction.read(LogicalDatastoreType.CONFIGURATION, nwInstanceIdentifier).get();
116             if (nwOptional.isPresent()) {
117                 nw = nwOptional.get();
118                 LOG.debug("readMdSal: network nodes: nwOptional.isPresent = true {}", nw.toString());
119             }
120         } catch (ExecutionException | InterruptedException e) {
121             LOG.error("readMdSal: Error reading topology {}", nwInstanceIdentifier);
122             readOnlyTransaction.close();
123             this.returnStructure.setRC(ResponseCodes.RESPONSE_FAILED);
124             throw new RuntimeException(
125                     "readMdSal: Error reading from operational store, topology : " + nwInstanceIdentifier + " :" + e);
126         }
127         readOnlyTransaction.close();
128         if (nw == null) {
129             LOG.error("readMdSal: network is null: {}", nwInstanceIdentifier);
130             return false;
131         }
132         this.allNodes = nw.getNode().stream()
133                 .sorted((node1, node2) -> node1.getNodeId().getValue().compareTo(node2.getNodeId().getValue()))
134                 .collect(Collectors.toList());
135         Network1 nw1 = nw.augmentation(Network1.class);
136         this.allLinks = nw1.getLink();
137         if ((this.allNodes == null) || this.allNodes.isEmpty()) {
138             LOG.error("readMdSal: no nodes ");
139             return false;
140         }
141         LOG.info("readMdSal: network nodes: {} nodes added", this.allNodes.size());
142         if ((this.allLinks == null) || this.allLinks.isEmpty()) {
143             LOG.error("readMdSal: no links ");
144             return false;
145         }
146         LOG.info("readMdSal: network links: {} links added", this.allLinks.size());
147         return true;
148     }
149
150     private boolean analyzeNw() {
151         LOG.debug("analyzeNw: allNodes size {}, allLinks size {}", this.allNodes.size(), this.allLinks.size());
152         for (Node node : this.allNodes) {
153             validateNode(node);
154         }
155         LOG.debug("analyzeNw: allPceNodes size {} : {}", this.allPceNodes.size(), this.allPceNodes.toString());
156         if ((this.aendPceNode == null) || (this.zendPceNode == null)) {
157             LOG.error("analyzeNw: Error in reading nodes: A or Z do not present in the network");
158             return false;
159         }
160         for (Link link : this.allLinks) {
161             validateLink(link);
162         }
163         LOG.debug("analyzeNw: AddLinks size {}, DropLinks size {}", this.addLinks.size(), this.dropLinks.size());
164         // debug prints
165         LOG.info("analyzeNw: AZSrgs size = {}", this.azSrgs.size());
166         for (NodeId srg : this.azSrgs) {
167             LOG.info("analyzeNw: A/Z Srgs SRG = {}", srg.getValue());
168         }
169         // debug prints
170         for (PceLink link : this.addLinks) {
171             filterAddLinks(link);
172         }
173         for (PceLink link : this.dropLinks) {
174             filterDropLinks(link);
175         }
176         LOG.info("analyzeNw: allPceNodes size {}, allPceLinks size {}", this.allPceNodes.size(), this.allPceLinks
177                 .size());
178         return true;
179     }
180
181     private boolean filterAddLinks(PceLink pcelink) {
182         NodeId nodeId = pcelink.getSourceId();
183         if (this.azSrgs.contains(nodeId)) {
184             this.allPceLinks.put(pcelink.getLinkId(), pcelink);
185             this.allPceNodes.get(nodeId).addOutgoingLink(pcelink);
186             LOG.info("analyzeNw: Add_LINK added to source and to allPceLinks {}", pcelink.getLinkId().toString());
187             return true;
188         }
189         // remove the SRG from PceNodes, as it is not directly connected to A/Z
190         this.allPceNodes.remove(nodeId);
191         LOG.debug("analyzeNw: SRG removed {}", nodeId.getValue());
192         return false;
193     }
194
195     private boolean filterDropLinks(PceLink pcelink) {
196         NodeId nodeId = pcelink.getDestId();
197         if (this.azSrgs.contains(nodeId)) {
198             this.allPceLinks.put(pcelink.getLinkId(), pcelink);
199             this.allPceNodes.get(nodeId).addOutgoingLink(pcelink);
200             LOG.info("analyzeNw: Drop_LINK added to dest and to allPceLinks {}", pcelink.getLinkId().toString());
201             return true;
202         }
203         // remove the SRG from PceNodes, as it is not directly connected to A/Z
204         this.allPceNodes.remove(pcelink.getDestId());
205         LOG.debug("analyzeNw: SRG removed {}", nodeId.getValue());
206         return false;
207     }
208
209     private boolean validateLink(Link link) {
210         LOG.info("validateLink: link {} ", link.toString());
211         NodeId sourceId = link.getSource().getSourceNode();
212         NodeId destId = link.getDestination().getDestNode();
213         PceNode source = this.allPceNodes.get(sourceId);
214         PceNode dest = this.allPceNodes.get(destId);
215         if (source == null) {
216             LOG.warn("validateLink: source node is rejected by node validation - {}", link.getSource().getSourceNode()
217                     .getValue());
218             return false;
219         }
220         if (dest == null) {
221             LOG.warn("validateLink: dest node is rejected by node validation - {}", link.getDestination().getDestNode()
222                     .getValue());
223             return false;
224         }
225         PceLink pcelink = new PceLink(link);
226         if (!pcelink.isValid()) {
227             LOG.error(" validateLink: Link is ignored due errors in network data ");
228             return false;
229         }
230         LinkId linkId = pcelink.getLinkId();
231         switch (pcelink.getLinkType()) {
232             case ROADMTOROADM :
233                 this.allPceLinks.put(linkId, pcelink);
234                 source.addOutgoingLink(pcelink);
235                 LOG.info("validateLink: ROADMTOROADM-LINK added to allPceLinks {}", pcelink.toString());
236                 break;
237             case EXPRESSLINK :
238                 this.allPceLinks.put(linkId, pcelink);
239                 source.addOutgoingLink(pcelink);
240                 LOG.info("validateLink: EXPRESS-LINK added to allPceLinks {}", pcelink.toString());
241                 break;
242             case ADDLINK :
243                 pcelink.setClient(source.getRdmSrgClient(pcelink.getSourceTP().toString(), true));
244                 this.addLinks.add(pcelink);
245                 LOG.info("validateLink: ADD-LINK saved  {}", pcelink.toString());
246                 break;
247             case DROPLINK :
248                 pcelink.setClient(dest.getRdmSrgClient(pcelink.getDestTP().toString(), false));
249                 this.dropLinks.add(pcelink);
250                 LOG.info("validateLink: DROP-LINK saved  {}", pcelink.toString());
251                 break;
252             case XPONDERINPUT :
253                 this.azSrgs.add(sourceId);
254                 // store separately all SRG links directly connected to A/Z
255                 if (!dest.checkTP(pcelink.getDestTP().toString())) {
256                     LOG.debug("validateLink: XPONDER-INPUT is rejected as NW port is busy - {} ", pcelink.toString());
257                     return false;
258                 }
259                 pcelink.setClient(dest.getXpdrClient(pcelink.getDestTP().toString()));
260                 this.allPceLinks.put(linkId, pcelink);
261                 source.addOutgoingLink(pcelink);
262                 LOG.info("validateLink: XPONDER-INPUT link added to allPceLinks {}", pcelink.toString());
263                 break;
264             case XPONDEROUTPUT :
265                 // does it mean XPONDER==>>SRG ?
266                 this.azSrgs.add(destId);
267                 // store separately all SRG links directly connected to A/Z
268                 if (!source.checkTP(pcelink.getSourceTP().toString())) {
269                     LOG.debug("validateLink: XPONDER-OUTPUT is rejected as NW port is busy - {} ", pcelink.toString());
270                     return false;
271                 }
272                 pcelink.setClient(source.getXpdrClient(pcelink.getSourceTP().toString()));
273                 this.allPceLinks.put(linkId, pcelink);
274                 source.addOutgoingLink(pcelink);
275                 LOG.info("validateLink: XPONDER-OUTPUT link added to allPceLinks {}", pcelink.toString());
276                 break;
277             default:
278                 LOG.warn("validateLink: link type is not supported {}", pcelink.toString());
279         }
280         return true;
281     }
282
283     private boolean validateNode(Node node) {
284         String supNodeId = "";
285         OpenroadmNodeType nodeType = null;
286         NodeId nodeId = null;
287         if (node == null) {
288             LOG.error("validateNode: node is null, ignored ");
289             return false;
290         }
291         try {
292             // TODO: supporting IDs exist as a List. this code takes just the first element
293             nodeId = node.getNodeId();
294             supNodeId = node.getSupportingNode().get(0).getNodeRef().getValue();
295             if (supNodeId.equals("")) {
296                 LOG.error("validateNode: Supporting node for node: [{}]. Node is ignored", nodeId.getValue());
297                 return false;
298             }
299             // extract node type
300             Node1 node1 = node.augmentation(Node1.class);
301             if (node1 == null) {
302                 LOG.error("validateNode: no Node1 (type) Augmentation for node: [{}]. Node is ignored", nodeId
303                         .getValue());
304                 return false;
305             }
306             nodeType = node1.getNodeType();
307             /** Catch exception 'RuntimeException' is not allowed. [IllegalCatch]. */
308         } catch (NullPointerException e) {
309             LOG.error("validateNode: Error reading supporting node or node type for node '{}'", nodeId, e);
310             return false;
311         }
312         if (nodeType == OpenroadmNodeType.XPONDER) {
313             // Detect A and Z
314             if (supNodeId.equals(this.anodeId) || (supNodeId.equals(this.znodeId))) {
315                 LOG.info("validateNode: A or Z node detected == {}", node.getNodeId().getValue());
316             } else {
317                 LOG.warn("validateNode: XPONDER is ignored == {}", node.getNodeId().getValue());
318                 return false;
319             }
320         }
321         switch (validateNodeConstraints(nodeId.getValue(), supNodeId)) {
322             case HARD_EXCLUDE :
323                 LOG.info("validateNode: constraints : node is ignored == {}", nodeId.getValue());
324                 return false;
325             default:
326                 break;
327         }
328         PceNode pceNode = new PceNode(node, nodeType, nodeId);
329         if (!pceNode.isValid()) {
330             LOG.error(" validateNode: Node is ignored due errors in network data ");
331             return false;
332         }
333         if (supNodeId.equals(this.anodeId)) {
334             if (this.aendPceNode == null) {
335                 if (endPceNode(nodeType, nodeId, pceNode, true)) {
336                     if (!pceNode.isValid()) {
337                         LOG.error("validateNode: There are no available wavelengths in node {}", nodeId.getValue());
338                         return false;
339                     }
340                     this.aendPceNode = pceNode;
341                 }
342             } else {
343                 LOG.warn("aendPceNode already gets: {}", this.aendPceNode);
344             }
345         }
346         if (supNodeId.equals(this.znodeId)) {
347             if (this.zendPceNode == null) {
348                 if (endPceNode(nodeType, nodeId, pceNode, false)) {
349                     if (!pceNode.isValid()) {
350                         LOG.error("validateNode: There are no available wavelengths in node {}", nodeId.getValue());
351                         return false;
352                     }
353                     this.zendPceNode = pceNode;
354                 }
355             } else {
356                 LOG.warn("zendPceNode already gets: {}", this.zendPceNode);
357             }
358         }
359         pceNode.initWLlist();
360         if (!pceNode.isValid()) {
361             LOG.error("validateNode: There are no available wavelengths in node {}", nodeId.getValue());
362             return false;
363         }
364         this.allPceNodes.put(nodeId, pceNode);
365         LOG.debug("validateNode: node is saved {}", nodeId.getValue());
366         return true;
367     }
368
369     private Boolean endPceNode(OpenroadmNodeType openroadmNodeType, NodeId nodeId, PceNode pceNode, Boolean aend) {
370         Boolean add = true;
371         switch (openroadmNodeType) {
372             case SRG :
373                 pceNode.initSrgTps();
374                 this.azSrgs.add(nodeId);
375                 break;
376             case XPONDER :
377                 pceNode.initXndrTps();
378                 break;
379             default:
380                 add = false;
381                 LOG.warn("endPceNode: Node {} is not SRG or XPONDER !", nodeId);
382                 break;
383         }
384         return add;
385     }
386
387     private NodeConstraint validateNodeConstraints(String nodeId, String supNodeId) {
388         if (this.pceHardConstraints.getExcludeNodes().contains(nodeId)) {
389             return NodeConstraint.HARD_EXCLUDE;
390         }
391         if (this.pceHardConstraints.getExcludeNodes().contains(supNodeId)) {
392             return NodeConstraint.HARD_EXCLUDE;
393         }
394         if (this.pceHardConstraints.getIncludeNodes().contains(nodeId)) {
395             return NodeConstraint.HARD_INCLUDE;
396         }
397         if (this.pceHardConstraints.getIncludeNodes().contains(supNodeId)) {
398             return NodeConstraint.HARD_INCLUDE;
399         }
400         return NodeConstraint.NONE;
401     }
402
403     public PceNode getaPceNode() {
404         return this.aendPceNode;
405     }
406
407     public PceNode getzPceNode() {
408         return this.zendPceNode;
409     }
410
411     public Map<NodeId, PceNode> getAllPceNodes() {
412         return this.allPceNodes;
413     }
414
415     public Map<LinkId, PceLink> getAllPceLinks() {
416         return this.allPceLinks;
417     }
418 }