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