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