Merge "Updated OLM module to support 7.1 devices"
[transportpce.git] / pce / src / main / java / org / opendaylight / transportpce / pce / graph / PostAlgoPathValidator.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.graph;
10
11 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
12 import java.util.ArrayList;
13 import java.util.Arrays;
14 import java.util.BitSet;
15 import java.util.Collections;
16 import java.util.HashMap;
17 import java.util.List;
18 import java.util.Map;
19 import org.jgrapht.GraphPath;
20 import org.opendaylight.transportpce.common.ResponseCodes;
21 import org.opendaylight.transportpce.common.StringConstants;
22 import org.opendaylight.transportpce.common.fixedflex.GridConstant;
23 import org.opendaylight.transportpce.common.fixedflex.GridUtils;
24 import org.opendaylight.transportpce.pce.constraints.PceConstraints;
25 import org.opendaylight.transportpce.pce.constraints.PceConstraints.ResourcePair;
26 import org.opendaylight.transportpce.pce.model.SpectrumAssignment;
27 import org.opendaylight.transportpce.pce.networkanalyzer.PceNode;
28 import org.opendaylight.transportpce.pce.networkanalyzer.PceResult;
29 import org.opendaylight.yang.gen.v1.http.org.openroadm.network.types.rev200529.OpenroadmLinkType;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.rev180226.NodeId;
31 import org.opendaylight.yangtools.yang.common.Uint16;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 public class PostAlgoPathValidator {
36     /* Logging. */
37     private static final Logger LOG = LoggerFactory.getLogger(PostAlgoPathValidator.class);
38
39     private static final double MIN_OSNR_W100G = 17;
40     private static final double TRX_OSNR = 33;
41     private static final double ADD_OSNR = 30;
42     public static final Long CONST_OSNR = 1L;
43     public static final double SYS_MARGIN = 0;
44
45     @SuppressFBWarnings(
46         value = "SF_SWITCH_FALLTHROUGH",
47         justification = "intentional fallthrough")
48     public PceResult checkPath(GraphPath<String, PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes,
49         PceResult pceResult, PceConstraints pceHardConstraints, String serviceType) {
50
51         // check if the path is empty
52         if (path.getEdgeList().isEmpty()) {
53             pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
54             return pceResult;
55         }
56         int tribSlotNb = 1;
57         int spectralWidthSlotNumber = GridConstant.SPECTRAL_WIDTH_SLOT_NUMBER_MAP
58             .getOrDefault(serviceType, GridConstant.NB_SLOTS_100G);
59         SpectrumAssignment spectrumAssignment = null;
60         //variable to deal with 1GE (Nb=1) and 10GE (Nb=10) cases
61         switch (serviceType) {
62             case StringConstants.SERVICE_TYPE_400GE:
63                 spectralWidthSlotNumber = GridConstant.SPECTRAL_WIDTH_SLOT_NUMBER_MAP
64                     .getOrDefault(serviceType, GridConstant.NB_SLOTS_400G);
65             //fallthrough
66             case StringConstants.SERVICE_TYPE_100GE:
67             case StringConstants.SERVICE_TYPE_OTU4:
68                 spectrumAssignment = getSpectrumAssignment(path,
69                         allPceNodes, spectralWidthSlotNumber);
70                 pceResult.setServiceType(serviceType);
71                 if (spectrumAssignment.getBeginIndex() == 0 && spectrumAssignment.getStopIndex() == 0) {
72                     pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
73                     pceResult.setLocalCause(PceResult.LocalCause.NO_PATH_EXISTS);
74                     return pceResult;
75                 }
76                 if (spectrumAssignment.isFlexGrid()) {
77                     LOG.info("Spectrum assignment flexgrid mode");
78                     pceResult.setResultWavelength(GridConstant.IRRELEVANT_WAVELENGTH_NUMBER);
79                 } else {
80                     LOG.info("Spectrum assignment fixedgrid mode");
81                     pceResult.setResultWavelength(
82                             GridUtils.getWaveLengthIndexFromSpectrumAssigment(spectrumAssignment.getBeginIndex()));
83                 }
84                 pceResult.setMinFreq(GridUtils.getStartFrequencyFromIndex(spectrumAssignment.getBeginIndex()));
85                 pceResult.setMaxFreq(GridUtils.getStopFrequencyFromIndex(spectrumAssignment.getStopIndex()));
86                 LOG.info("In PostAlgoPathValidator: spectrum assignment found {} {}", spectrumAssignment, path);
87
88                 // Check the OSNR
89                 if (!checkOSNR(path)) {
90                     pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
91                     pceResult.setLocalCause(PceResult.LocalCause.OUT_OF_SPEC_OSNR);
92                     return pceResult;
93                 }
94
95                 // Check if MaxLatency is defined in the hard constraints
96                 if ((pceHardConstraints.getMaxLatency() != -1)
97                         && (!checkLatency(pceHardConstraints.getMaxLatency(), path))) {
98                     pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
99                     pceResult.setLocalCause(PceResult.LocalCause.TOO_HIGH_LATENCY);
100                     return pceResult;
101                 }
102
103                 // Check if nodes are included in the hard constraints
104                 if (!checkInclude(path, pceHardConstraints)) {
105                     pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
106                     pceResult.setLocalCause(PceResult.LocalCause.HD_NODE_INCLUDE);
107                     return pceResult;
108                 }
109
110                 // TODO here other post algo validations can be added
111                 // more data can be sent to PceGraph module via PceResult structure if required
112
113                 pceResult.setRC(ResponseCodes.RESPONSE_OK);
114                 pceResult.setLocalCause(PceResult.LocalCause.NONE);
115                 break;
116             case StringConstants.SERVICE_TYPE_10GE:
117                 tribSlotNb = 8;
118             //fallthrough
119             case StringConstants.SERVICE_TYPE_1GE:
120                 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
121                 pceResult.setServiceType(serviceType);
122                 Map<String, Uint16> tribPort = chooseTribPort(path, allPceNodes);
123                 Map<String, List<Uint16>> tribSlot = chooseTribSlot(path, allPceNodes, tribSlotNb);
124
125                 if (tribPort != null && tribSlot != null) {
126                     pceResult.setResultTribPort(tribPort);
127                     pceResult.setResultTribSlot(tribSlot);
128                     pceResult.setResultTribSlotNb(tribSlotNb);
129                     pceResult.setRC(ResponseCodes.RESPONSE_OK);
130                     LOG.info("In PostAlgoPathValidator: found TribPort {} - tribSlot {} - tribSlotNb {}",
131                         tribPort, tribSlot, tribSlotNb);
132                 }
133                 break;
134             case StringConstants.SERVICE_TYPE_ODU4:
135                 pceResult.setRC(ResponseCodes.RESPONSE_OK);
136                 LOG.info("In PostAlgoPathValidator: ODU4 path found {}", path);
137                 break;
138             default:
139                 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
140                 LOG.warn("In PostAlgoPathValidator checkPath: unsupported serviceType {} found {}",
141                     serviceType, path);
142                 break;
143         }
144         return pceResult;
145     }
146
147     // Check the latency
148     private boolean checkLatency(Long maxLatency, GraphPath<String, PceGraphEdge> path) {
149         double latency = 0;
150
151         for (PceGraphEdge edge : path.getEdgeList()) {
152             try {
153                 latency += edge.link().getLatency();
154                 LOG.debug("- In checkLatency: latency of {} = {} units", edge.link().getLinkId().getValue(), latency);
155             } catch (NullPointerException e) {
156                 LOG.warn("- In checkLatency: the link {} does not contain latency field",
157                     edge.link().getLinkId().getValue());
158             }
159         }
160         return (latency < maxLatency);
161     }
162
163     // Check the inclusion if it is defined in the hard constraints
164     private boolean checkInclude(GraphPath<String, PceGraphEdge> path, PceConstraints pceHardConstraintsInput) {
165         List<ResourcePair> listToInclude = pceHardConstraintsInput.getListToInclude();
166         if (listToInclude.isEmpty()) {
167             return true;
168         }
169
170         List<PceGraphEdge> pathEdges = path.getEdgeList();
171         LOG.debug(" in checkInclude vertex list: [{}]", path.getVertexList());
172
173         List<String> listOfElementsSubNode = new ArrayList<>();
174         listOfElementsSubNode.add(pathEdges.get(0).link().getsourceNetworkSupNodeId());
175         listOfElementsSubNode.addAll(listOfElementsBuild(pathEdges, PceConstraints.ResourceType.NODE,
176             pceHardConstraintsInput));
177
178         List<String> listOfElementsCLLI = new ArrayList<>();
179         listOfElementsCLLI.add(pathEdges.get(0).link().getsourceCLLI());
180         listOfElementsCLLI.addAll(listOfElementsBuild(pathEdges, PceConstraints.ResourceType.CLLI,
181             pceHardConstraintsInput));
182
183         List<String> listOfElementsSRLG = new ArrayList<>();
184         // first link is XPONDEROUTPUT, no SRLG for it
185         listOfElementsSRLG.add("NONE");
186         listOfElementsSRLG.addAll(listOfElementsBuild(pathEdges, PceConstraints.ResourceType.SRLG,
187             pceHardConstraintsInput));
188
189         // validation: check each type for each element
190         for (ResourcePair next : listToInclude) {
191             int indx = -1;
192             switch (next.getType()) {
193                 case NODE:
194                     if (listOfElementsSubNode.contains(next.getName())) {
195                         indx = listOfElementsSubNode.indexOf(next.getName());
196                     }
197                     break;
198                 case SRLG:
199                     if (listOfElementsSRLG.contains(next.getName())) {
200                         indx = listOfElementsSRLG.indexOf(next.getName());
201                     }
202                     break;
203                 case CLLI:
204                     if (listOfElementsCLLI.contains(next.getName())) {
205                         indx = listOfElementsCLLI.indexOf(next.getName());
206                     }
207                     break;
208                 default:
209                     LOG.warn(" in checkInclude vertex list unsupported resource type: [{}]", next.getType());
210             }
211
212             if (indx < 0) {
213                 LOG.debug(" in checkInclude stopped : {} ", next.getName());
214                 return false;
215             }
216
217             LOG.debug(" in checkInclude next found {} in {}", next.getName(), path.getVertexList());
218
219             listOfElementsSubNode.subList(0, indx).clear();
220             listOfElementsCLLI.subList(0, indx).clear();
221             listOfElementsSRLG.subList(0, indx).clear();
222         }
223
224         LOG.info(" in checkInclude passed : {} ", path.getVertexList());
225         return true;
226     }
227
228     private List<String> listOfElementsBuild(List<PceGraphEdge> pathEdges, PceConstraints.ResourceType type,
229         PceConstraints pceHardConstraints) {
230
231         List<String> listOfElements = new ArrayList<>();
232         for (PceGraphEdge link : pathEdges) {
233             switch (type) {
234                 case NODE:
235                     listOfElements.add(link.link().getdestNetworkSupNodeId());
236                     break;
237                 case CLLI:
238                     listOfElements.add(link.link().getdestCLLI());
239                     break;
240                 case SRLG:
241                     if (link.link().getlinkType() != OpenroadmLinkType.ROADMTOROADM) {
242                         listOfElements.add("NONE");
243                         break;
244                     }
245                     // srlg of link is List<Long>. But in this algo we need string representation of
246                     // one SRLG
247                     // this should be any SRLG mentioned in include constraints if any of them if
248                     // mentioned
249                     boolean found = false;
250                     for (Long srlg : link.link().getsrlgList()) {
251                         String srlgStr = String.valueOf(srlg);
252                         if (pceHardConstraints.getSRLGnames().contains(srlgStr)) {
253                             listOfElements.add(srlgStr);
254                             LOG.info("listOfElementsBuild. FOUND SRLG {} in link {}", srlgStr, link.link());
255                             found = true;
256                         }
257                     }
258                     if (!found) {
259                         // there is no specific srlg to include. thus add to list just the first one
260                         listOfElements.add("NONE");
261                     }
262                     break;
263                 default:
264                     LOG.debug("listOfElementsBuild unsupported resource type");
265             }
266         }
267         return listOfElements;
268     }
269
270     private Map<String, Uint16> chooseTribPort(GraphPath<String,
271         PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes) {
272         LOG.info("In choosetribPort: edgeList = {} ", path.getEdgeList());
273         Map<String, Uint16> tribPortMap = new HashMap<>();
274
275         for (PceGraphEdge edge : path.getEdgeList()) {
276             NodeId linkSrcNode = edge.link().getSourceId();
277             String linkSrcTp = edge.link().getSourceTP().toString();
278             NodeId linkDestNode = edge.link().getDestId();
279             String linkDestTp = edge.link().getDestTP().toString();
280             PceNode pceOtnNodeSrc = allPceNodes.get(linkSrcNode);
281             PceNode pceOtnNodeDest = allPceNodes.get(linkDestNode);
282             List<Uint16> srcTpnPool = pceOtnNodeSrc.getAvailableTribPorts().get(linkSrcTp);
283             List<Uint16> destTpnPool = pceOtnNodeDest.getAvailableTribPorts().get(linkDestTp);
284             List<Uint16> commonEdgeTpnPool = new ArrayList<>();
285             for (Uint16 integer : srcTpnPool) {
286                 if (destTpnPool.contains(integer)) {
287                     commonEdgeTpnPool.add(integer);
288                 }
289             }
290             Collections.sort(commonEdgeTpnPool);
291             if (!commonEdgeTpnPool.isEmpty()) {
292                 tribPortMap.put(edge.link().getLinkId().getValue(), commonEdgeTpnPool.get(0));
293             }
294         }
295         tribPortMap.forEach((k,v) -> LOG.info("TribPortMap : k = {}, v = {}", k, v));
296         return tribPortMap;
297     }
298
299     private Map<String, List<Uint16>> chooseTribSlot(GraphPath<String,
300         PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes, int nbSlot) {
301         LOG.info("In choosetribSlot: edgeList = {} ", path.getEdgeList());
302         Map<String, List<Uint16>> tribSlotMap = new HashMap<>();
303
304         for (PceGraphEdge edge : path.getEdgeList()) {
305             NodeId linkSrcNode = edge.link().getSourceId();
306             String linkSrcTp = edge.link().getSourceTP().toString();
307             NodeId linkDestNode = edge.link().getDestId();
308             String linkDestTp = edge.link().getDestTP().toString();
309             PceNode pceOtnNodeSrc = allPceNodes.get(linkSrcNode);
310             PceNode pceOtnNodeDest = allPceNodes.get(linkDestNode);
311             List<Uint16> srcTsPool = pceOtnNodeSrc.getAvailableTribSlots().get(linkSrcTp);
312             List<Uint16> destTsPool = pceOtnNodeDest.getAvailableTribSlots().get(linkDestTp);
313             List<Uint16> commonEdgeTsPool = new ArrayList<>();
314             List<Uint16> tribSlotList = new ArrayList<>();
315             for (Uint16 integer : srcTsPool) {
316                 if (destTsPool.contains(integer)) {
317                     commonEdgeTsPool.add(integer);
318                 }
319             }
320             Collections.sort(commonEdgeTsPool);
321             boolean discontinue = true;
322             int index = 0;
323             while (discontinue && (commonEdgeTsPool.size() - index >= nbSlot)) {
324                 discontinue = false;
325                 Integer val = commonEdgeTsPool.get(index).toJava();
326                 for (int i = 0; i < nbSlot; i++) {
327                     if (commonEdgeTsPool.get(index + i).equals(Uint16.valueOf(val + i))) {
328                         tribSlotList.add(commonEdgeTsPool.get(index + i));
329                     } else {
330                         discontinue = true;
331                         tribSlotList.clear();
332                         index += i;
333                         break;
334                     }
335                 }
336             }
337             tribSlotMap.put(edge.link().getLinkId().getValue(), tribSlotList);
338         }
339         tribSlotMap.forEach((k,v) -> LOG.info("TribSlotMap : k = {}, v = {}", k, v));
340         return tribSlotMap;
341     }
342
343     // Check the path OSNR
344     private boolean checkOSNR(GraphPath<String, PceGraphEdge> path) {
345         double linkOsnrDb;
346         double osnrDb = 0;
347         LOG.info("- In checkOSNR: OSNR of the transmitter = {} dB", TRX_OSNR);
348         LOG.info("- In checkOSNR: add-path incremental OSNR = {} dB", ADD_OSNR);
349         double inverseLocalOsnr = getInverseOsnrLinkLu(TRX_OSNR) + getInverseOsnrLinkLu(ADD_OSNR);
350         for (PceGraphEdge edge : path.getEdgeList()) {
351             if (edge.link().getlinkType() == OpenroadmLinkType.ROADMTOROADM) {
352                 // link OSNR in dB
353                 linkOsnrDb = edge.link().getosnr();
354                 LOG.info("- In checkOSNR: OSNR of {} = {} dB", edge.link().getLinkId().getValue(), linkOsnrDb);
355                 // 1 over the local OSNR, in linear units
356                 inverseLocalOsnr += getInverseOsnrLinkLu(linkOsnrDb);
357             }
358         }
359         try {
360             osnrDb = getOsnrDb(1 / inverseLocalOsnr);
361         } catch (ArithmeticException e) {
362             LOG.debug("In checkOSNR: OSNR is equal to 0 and the number of links is: {}", path.getEdgeList().size());
363             return false;
364         }
365         LOG.info("In checkOSNR: OSNR of the path is {} dB", osnrDb);
366         return ((osnrDb + SYS_MARGIN) > MIN_OSNR_W100G);
367     }
368
369     private double getOsnrDb(double osnrLu) {
370         double osnrDb;
371         osnrDb = 10 * Math.log10(osnrLu);
372         return osnrDb;
373     }
374
375     private double getInverseOsnrLinkLu(double linkOsnrDb) {
376         // 1 over the link OSNR, in linear units
377         double linkOsnrLu;
378         linkOsnrLu = Math.pow(10, (linkOsnrDb / 10.0));
379         LOG.debug("In retrieveosnr: the inverse of link osnr is {} (Linear Unit)", linkOsnrLu);
380         return (CONST_OSNR / linkOsnrLu);
381     }
382
383     /**
384      * Get spectrum assignment for path.
385      *
386      * @param path                    the path for which we get spectrum assignment.
387      * @param allPceNodes             all optical nodes.
388      * @param spectralWidthSlotNumber number of slot for spectral width. Depends on
389      *                                service type.
390      * @return a spectrum assignment object which contains begin and end index. If
391      *         no spectrum assignment found, beginIndex = stopIndex = 0
392      */
393     private SpectrumAssignment getSpectrumAssignment(GraphPath<String, PceGraphEdge> path,
394             Map<NodeId, PceNode> allPceNodes, int spectralWidthSlotNumber) {
395         byte[] freqMap = new byte[GridConstant.NB_OCTECTS];
396         Arrays.fill(freqMap, (byte) GridConstant.AVAILABLE_SLOT_VALUE);
397         BitSet result = BitSet.valueOf(freqMap);
398         boolean isFlexGrid = true;
399         LOG.info("Processing path {} with length {}", path, path.getLength());
400         BitSet pceNodeFreqMap;
401         for (PceGraphEdge edge : path.getEdgeList()) {
402             LOG.info("Processing source {} ", edge.link().getSourceId());
403             if (allPceNodes.containsKey(edge.link().getSourceId())) {
404                 PceNode pceNode = allPceNodes.get(edge.link().getSourceId());
405                 LOG.info("Processing PCE node {}", pceNode);
406                 if (StringConstants.OPENROADM_DEVICE_VERSION_1_2_1.equals(pceNode.getVersion())
407                         || pceNode.getSlotWidthGranularity().compareTo(GridConstant.SLOT_WIDTH_50) == 0) {
408                     LOG.info("Node {}: version is {} and slot width granularity is {} -> fixed grid mode",
409                             pceNode.getNodeId(), pceNode.getVersion(), pceNode.getSlotWidthGranularity());
410                     isFlexGrid = false;
411                 }
412                 pceNodeFreqMap = pceNode.getBitSetData();
413                 LOG.debug("Pce node bitset {}", pceNodeFreqMap);
414                 if (pceNodeFreqMap != null) {
415                     result.and(pceNodeFreqMap);
416                     LOG.debug("intermediate bitset {}", result);
417                 }
418             }
419         }
420         LOG.debug("Bitset result {}", result);
421         return computeBestSpectrumAssignment(result, spectralWidthSlotNumber, isFlexGrid);
422     }
423
424     /**
425      * Compute spectrum assignment from spectrum occupation for spectral width.
426      *
427      * @param spectrumOccupation      the spectrum occupation BitSet.
428      * @param spectralWidthSlotNumber the nb slots for spectral width.
429      * @param isFlexGrid              true if flexible grid, false otherwise.
430      * @return a spectrum assignment object which contains begin and stop index. If
431      *         no spectrum assignment found, beginIndex = stopIndex = 0
432      */
433     private SpectrumAssignment computeBestSpectrumAssignment(BitSet spectrumOccupation, int spectralWidthSlotNumber,
434             boolean isFlexGrid) {
435         SpectrumAssignment spectrumAssignment = new SpectrumAssignment(0, 0);
436         spectrumAssignment.setFlexGrid(isFlexGrid);
437         BitSet referenceBitSet = new BitSet(spectralWidthSlotNumber);
438         referenceBitSet.set(0, spectralWidthSlotNumber);
439         int nbSteps = 1;
440         if (isFlexGrid) {
441             nbSteps = spectralWidthSlotNumber;
442         }
443         //higher is the frequency, smallest is the wavelength number
444         //in operational, the allocation is done through wavelength starting from the smallest
445         //so we have to loop from the last element of the spectrum occupation
446         for (int i = spectrumOccupation.size(); i >= spectralWidthSlotNumber; i -= nbSteps) {
447             if (spectrumOccupation.get(i - spectralWidthSlotNumber, i).equals(referenceBitSet)) {
448                 spectrumAssignment.setBeginIndex(i - spectralWidthSlotNumber);
449                 spectrumAssignment.setStopIndex(i - 1);
450                 break;
451             }
452         }
453         return spectrumAssignment;
454     }
455
456 }