Technical debt - Fix PCE sonar issues
[transportpce.git] / pce / src / main / java / org / opendaylight / transportpce / pce / gnpy / GnpyResult.java
1 /*
2  * Copyright © 2018 Orange, 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.gnpy;
10
11 import com.google.gson.stream.JsonReader;
12 import java.io.ByteArrayInputStream;
13 import java.io.IOException;
14 import java.io.InputStream;
15 import java.io.InputStreamReader;
16 import java.math.BigDecimal;
17 import java.nio.charset.StandardCharsets;
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.HashMap;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Map.Entry;
25 import java.util.Optional;
26 import java.util.stream.Collectors;
27 import javax.annotation.Nonnull;
28 import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
29 import org.opendaylight.mdsal.binding.dom.codec.spi.BindingDOMCodecServices;
30 import org.opendaylight.yang.gen.v1.gnpy.path.rev200909.Result;
31 import org.opendaylight.yang.gen.v1.gnpy.path.rev200909.explicit.route.hop.type.NumUnnumHop;
32 import org.opendaylight.yang.gen.v1.gnpy.path.rev200909.generic.path.properties.path.properties.PathMetric;
33 import org.opendaylight.yang.gen.v1.gnpy.path.rev200909.generic.path.properties.path.properties.PathRouteObjects;
34 import org.opendaylight.yang.gen.v1.gnpy.path.rev200909.result.Response;
35 import org.opendaylight.yang.gen.v1.gnpy.path.rev200909.result.response.response.type.NoPathCase;
36 import org.opendaylight.yang.gen.v1.gnpy.path.rev200909.result.response.response.type.PathCase;
37 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.routing.constraints.rev171017.constraints.sp.co.routing.or.general.General;
38 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.routing.constraints.rev171017.constraints.sp.co.routing.or.general.GeneralBuilder;
39 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.routing.constraints.rev171017.constraints.sp.co.routing.or.general.general.Include;
40 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.routing.constraints.rev171017.constraints.sp.co.routing.or.general.general.IncludeBuilder;
41 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.routing.constraints.rev171017.constraints.sp.co.routing.or.general.general.include_.OrderedHops;
42 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.routing.constraints.rev171017.constraints.sp.co.routing.or.general.general.include_.OrderedHopsBuilder;
43 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.routing.constraints.rev171017.ordered.constraints.sp.HopType;
44 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.routing.constraints.rev171017.ordered.constraints.sp.HopTypeBuilder;
45 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.routing.constraints.rev171017.ordered.constraints.sp.hop.type.hop.type.NodeBuilder;
46 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.routing.constraints.rev171017.routing.constraints.sp.HardConstraints;
47 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.routing.constraints.rev171017.routing.constraints.sp.HardConstraintsBuilder;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
49 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
50 import org.opendaylight.yangtools.yang.binding.DataObject;
51 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
52 import org.opendaylight.yangtools.yang.common.QName;
53 import org.opendaylight.yangtools.yang.common.Uint16;
54 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
55 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
56 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
57 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
58 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
59 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
60 import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactorySupplier;
61 import org.opendaylight.yangtools.yang.data.codec.gson.JsonParserStream;
62 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
63 import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
64 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
65 import org.slf4j.Logger;
66 import org.slf4j.LoggerFactory;
67
68 /**
69  * Class for analyzing the result sent by GNPy.
70  *
71  * @author Ahmed Triki ( ahmed.triki@orange.com )
72  *
73  */
74
75 public class GnpyResult {
76
77     private static final Logger LOG = LoggerFactory.getLogger(GnpyResult.class);
78     private Response response = null;
79     private Map<String, IpAddress> mapNodeRefIp = new HashMap<>();
80     private EffectiveModelContext effectiveModelcontext;
81
82     public GnpyResult(String gnpyResponseString, GnpyTopoImpl gnpyTopo,
83             BindingDOMCodecServices bindingDOMCodecServices) throws GnpyException {
84         this.mapNodeRefIp = gnpyTopo.getMapNodeRefIp();
85         effectiveModelcontext = bindingDOMCodecServices.getRuntimeContext().getEffectiveModelContext();
86
87         // Create the data object
88         QName pathQname = QName.create("gnpy:path", "2020-09-09", "result");
89         LOG.debug("the Qname is {} / namesapce {} ; module {}; ", pathQname, pathQname.getNamespace(),
90             pathQname.getModule());
91         YangInstanceIdentifier yangId = YangInstanceIdentifier.of(pathQname);
92         DataObject dataObject = null;
93
94         // Create the object response
95         InputStream streamGnpyRespnse = new ByteArrayInputStream(gnpyResponseString.getBytes(StandardCharsets.UTF_8));
96         InputStreamReader gnpyResultReader = new InputStreamReader(streamGnpyRespnse,StandardCharsets.UTF_8);
97         JsonReader jsonReader = new JsonReader(gnpyResultReader);
98         Optional<NormalizedNode<? extends PathArgument, ?>> transformIntoNormalizedNode = parseInputJSON(jsonReader,
99             Result.class);
100         if (!transformIntoNormalizedNode.isPresent()) {
101             throw new GnpyException("In GnpyResult: the Normalized Node is not present");
102         }
103         NormalizedNode<? extends PathArgument, ?> normalizedNode = transformIntoNormalizedNode.get();
104         Entry<InstanceIdentifier<?>, DataObject> fromNormalizedNode = bindingDOMCodecServices
105                 .fromNormalizedNode(yangId, normalizedNode);
106
107         if (fromNormalizedNode != null) {
108             dataObject = fromNormalizedNode.getValue();
109         } else {
110             throw new GnpyException("In GnpyResult: the codec registry from the normalized node is null");
111         }
112         List<Response> responses = new ArrayList<>(((Result) dataObject).nonnullResponse().values());
113         if (responses.isEmpty()) {
114             throw new GnpyException("In GnpyResult: the response from GNpy is null!");
115         }
116         LOG.info("The response id is {}; ", responses.get(0).getResponseId());
117         this.response = responses.get(0);
118         analyzeResult();
119     }
120
121     public boolean getPathFeasibility() {
122         boolean isFeasible = false;
123         if (response != null) {
124             if (response.getResponseType() instanceof NoPathCase) {
125                 LOG.info("In GnpyResult: The path is not feasible ");
126             } else if (response.getResponseType() instanceof PathCase) {
127                 isFeasible = true;
128                 LOG.info("In GnpyResult: The path is feasible ");
129             }
130         }
131         return isFeasible;
132     }
133
134     public List<PathRouteObjects> analyzeResult() {
135         List<PathRouteObjects> pathRouteObjectList = null;
136         if (response != null) {
137             if (response.getResponseType() instanceof NoPathCase) {
138                 NoPathCase noPathCase = (NoPathCase) response.getResponseType();
139                 String noPathType = noPathCase.getNoPath().getNoPath();
140                 LOG.info("GNPy: No path - {}", noPathType);
141                 if (((noPathType.equals("NO_FEASIBLE_BAUDRATE_WITH_SPACING"))
142                     && (noPathType.equals("NO_FEASIBLE_MODE"))) && ((noPathType.equals("MODE_NOT_FEASIBLE"))
143                         && (noPathType.equals("NO_SPECTRUM")))) {
144                     Collection<PathMetric> pathMetricList = noPathCase.getNoPath()
145                             .getPathProperties().nonnullPathMetric().values();
146                     LOG.info("GNPy : path is not feasible : {}", noPathType);
147                     for (PathMetric pathMetric : pathMetricList) {
148                         String metricType = pathMetric.getMetricType().getSimpleName();
149                         BigDecimal accumulativeValue = pathMetric.getAccumulativeValue();
150                         LOG.info("Metric type {} // AccumulatriveValue {}", metricType, accumulativeValue);
151                     }
152                 }
153             } else if (response.getResponseType() instanceof PathCase) {
154                 LOG.info("GNPy : path is feasible");
155                 PathCase pathCase = (PathCase) response.getResponseType();
156                 Collection<PathMetric> pathMetricList = pathCase
157                         .getPathProperties().nonnullPathMetric().values();
158                 // Path metrics
159                 for (PathMetric pathMetric : pathMetricList) {
160                     String metricType = pathMetric.getMetricType().getSimpleName();
161                     BigDecimal accumulativeValue = pathMetric.getAccumulativeValue();
162                     LOG.info("Metric type {} // AccumulatriveValue {}", metricType, accumulativeValue);
163                 }
164                 // Path route objects
165                 pathRouteObjectList = pathCase.getPathProperties().getPathRouteObjects();
166                 LOG.info("in GnpyResult: finishing the computation of pathRouteObjectList");
167             }
168         }
169         return pathRouteObjectList;
170     }
171
172     public HardConstraints computeHardConstraintsFromGnpyPath(List<PathRouteObjects> pathRouteObjectList) {
173         HardConstraints hardConstraints = null;
174         // Includes the list of nodes in the GNPy computed path as constraints
175         // for the PCE
176         List<OrderedHops> orderedHopsList = new ArrayList<>();
177         int counter = 0;
178         for (PathRouteObjects pathRouteObjects : pathRouteObjectList) {
179             if (pathRouteObjects.getPathRouteObject().getType() instanceof NumUnnumHop) {
180                 NumUnnumHop numUnnumHop = (org.opendaylight.yang.gen.v1.gnpy.path.rev200909.explicit.route.hop.type
181                     .NumUnnumHop) pathRouteObjects.getPathRouteObject().getType();
182                 String nodeIp = numUnnumHop.getNumUnnumHop().getNodeId();
183                 try {
184                     IpAddress nodeIpAddress = new IpAddress(new Ipv4Address(nodeIp));
185                     // find the corresponding node-id (in ord-ntw) corresponding to nodeId (in gnpy response)
186                     String nodeId = findOrdNetworkNodeId(nodeIpAddress);
187                     if (nodeId != null) {
188                         org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.routing.constraints.rev171017
189                             .ordered.constraints.sp.hop.type.hop.type.Node node = new NodeBuilder().setNodeId(nodeId)
190                             .build();
191                         HopType hopType = new HopTypeBuilder().setHopType(node).build();
192                         OrderedHops orderedHops = new OrderedHopsBuilder()
193                                 .setHopNumber(Uint16.valueOf(counter)).setHopType(hopType)
194                             .build();
195                         orderedHopsList.add(orderedHops);
196                         counter++;
197                     }
198                 } catch (IllegalArgumentException e) {
199                     LOG.debug(" in GnpyResult: the element {} is not a ipv4Address ", nodeIp);
200                 }
201             }
202         }
203         Include include = new IncludeBuilder()
204                 .setOrderedHops(orderedHopsList.stream()
205                         .collect(Collectors.toMap(OrderedHops::key, orderedHops -> orderedHops)))
206                 .build();
207         General general = new GeneralBuilder().setInclude(include).build();
208         hardConstraints = new HardConstraintsBuilder().setCoRoutingOrGeneral(general).build();
209         return hardConstraints;
210     }
211
212     private String findOrdNetworkNodeId(IpAddress nodeIpAddress) {
213         Iterator<Map.Entry<String,IpAddress>> it = this.mapNodeRefIp.entrySet().iterator();
214         while (it.hasNext()) {
215             Entry<String, IpAddress> entry = it.next();
216             if (entry.getValue().equals(nodeIpAddress)) {
217                 return entry.getKey();
218             }
219         }
220         return null;
221     }
222
223     /**
224      * Parses the input json with concrete implementation of {@link JsonParserStream}.
225      * @param reader of the given JSON
226      * @throws Exception exception
227      */
228     private Optional<NormalizedNode<? extends YangInstanceIdentifier.PathArgument, ?>> parseInputJSON(JsonReader reader,
229         Class<? extends DataObject> objectClass) {
230         NormalizedNodeResult result = new NormalizedNodeResult();
231         try (NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
232             JsonParserStream jsonParser = JsonParserStream.create(streamWriter,
233                 JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02.getShared(effectiveModelcontext),
234                 effectiveModelcontext);) {
235             jsonParser.parse(reader);
236         } catch (IOException e) {
237             LOG.warn("GNPy: exception {} occured during parsing Json input stream", e.getMessage());
238             return Optional.empty();
239         }
240         return Optional.ofNullable(result.getResult());
241     }
242
243
244     /**
245      * Transforms the given input {@link NormalizedNode} into the given {@link DataObject}.
246      *
247      * @param <T> a generic
248      * @param normalizedNode a non null representation of the normalizedNode
249      * @param rootNode root node
250      * @param codecRegistry codec registry
251      * @return value of the binding Node Entry
252      */
253     @SuppressWarnings("unchecked")
254     public <T extends DataObject> Optional<T> getDataObject(@Nonnull NormalizedNode<?, ?> normalizedNode,
255         @Nonnull QName rootNode, BindingNormalizedNodeSerializer codecRegistry) {
256         if (normalizedNode != null) {
257             LOG.debug("GNPy: The codecRegistry is {}", codecRegistry);
258         } else {
259             LOG.warn("GNPy: The codecRegistry is null");
260         }
261         //Preconditions.checkNotNull(normalizedNode);
262         if (normalizedNode instanceof ContainerNode) {
263             YangInstanceIdentifier.PathArgument directChildIdentifier = YangInstanceIdentifier.of(rootNode)
264                 .getLastPathArgument();
265             Optional<NormalizedNode<?, ?>> directChild = NormalizedNodes.getDirectChild(normalizedNode,
266                 directChildIdentifier);
267             if (!directChild.isPresent()) {
268                 throw new IllegalStateException(String.format("Could not get the direct child of %s", rootNode));
269             }
270             normalizedNode = directChild.get();
271             LOG.debug("GNPy: the normalized node is {}", normalizedNode.getNodeType());
272         }
273         YangInstanceIdentifier rootNodeYangInstanceIdentifier = YangInstanceIdentifier.of(rootNode);
274         LOG.debug("GNPy: the root Node Yang Instance Identifier is {}", rootNodeYangInstanceIdentifier);
275         Map.Entry<?, ?> bindingNodeEntry = codecRegistry.fromNormalizedNode(rootNodeYangInstanceIdentifier,
276             normalizedNode);
277         if (bindingNodeEntry == null) {
278             LOG.debug("The binding Node Entry is null");
279             return Optional.empty();
280         }
281         return Optional.ofNullable((T) bindingNodeEntry.getValue());
282     }
283
284     public Response getResponse() {
285         return response;
286     }
287 }