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