exchange with GNPy to check path feasibility
[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.BufferedReader;
15 import java.io.ByteArrayInputStream;
16 import java.io.FileReader;
17 import java.io.IOException;
18 import java.io.InputStream;
19 import java.io.InputStreamReader;
20 import java.math.BigDecimal;
21 import java.nio.charset.StandardCharsets;
22 import java.util.Collections;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Optional;
26
27 import javassist.ClassPool;
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.gen.impl.StreamWriterGenerator;
33 import org.opendaylight.mdsal.binding.dom.codec.impl.BindingNormalizedNodeCodecRegistry;
34 import org.opendaylight.mdsal.binding.generator.impl.ModuleInfoBackedContext;
35 import org.opendaylight.mdsal.binding.generator.util.BindingRuntimeContext;
36 import org.opendaylight.mdsal.binding.generator.util.JavassistUtils;
37 import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
38 import org.opendaylight.yang.gen.v1.gnpy.path.rev190502.Result;
39 import org.opendaylight.yang.gen.v1.gnpy.path.rev190502.explicit.route.hop.type.num.unnum.hop.NumUnnumHop;
40 import org.opendaylight.yang.gen.v1.gnpy.path.rev190502.generic.path.properties.path.properties.PathMetric;
41 import org.opendaylight.yang.gen.v1.gnpy.path.rev190502.generic.path.properties.path.properties.PathRouteObjects;
42 import org.opendaylight.yang.gen.v1.gnpy.path.rev190502.result.Response;
43 import org.opendaylight.yang.gen.v1.gnpy.path.rev190502.result.response.response.type.NoPathCase;
44 import org.opendaylight.yang.gen.v1.gnpy.path.rev190502.result.response.response.type.PathCase;
45 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.routing.constraints.rev171017.constraints.sp.co.routing.or.general.General;
46 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.routing.constraints.rev171017.constraints.sp.co.routing.or.general.GeneralBuilder;
47 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.routing.constraints.rev171017.constraints.sp.co.routing.or.general.general.Include;
48 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.routing.constraints.rev171017.constraints.sp.co.routing.or.general.general.IncludeBuilder;
49 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.routing.constraints.rev171017.constraints.sp.co.routing.or.general.general.include_.OrderedHops;
50 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.routing.constraints.rev171017.constraints.sp.co.routing.or.general.general.include_.OrderedHopsBuilder;
51 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.routing.constraints.rev171017.ordered.constraints.sp.HopType;
52 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.routing.constraints.rev171017.ordered.constraints.sp.HopTypeBuilder;
53 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.routing.constraints.rev171017.ordered.constraints.sp.hop.type.hop.type.NodeBuilder;
54 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.routing.constraints.rev171017.routing.constraints.sp.HardConstraints;
55 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.routing.constraints.rev171017.routing.constraints.sp.HardConstraintsBuilder;
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.JsonParserStream;
66 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
67 import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
68 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
69
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
85     public GnpyResult(String gnpyResponseString) throws Exception {
86
87         // try {
88         // Optional<DataObject> dataObject;
89         // Create the schema context
90         final ModuleInfoBackedContext moduleContext = ModuleInfoBackedContext.create();
91         Iterable<? extends YangModuleInfo> moduleInfos;
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 = new BindingNormalizedNodeCodecRegistry(
99                 StreamWriterGenerator.create(JavassistUtils.forClassPool(ClassPool.getDefault())));
100         codecRegistry.onBindingRuntimeContextUpdated(bindingRuntimeContext);
101
102         // Create the data object
103         QName pathQname = QName.create("gnpy:path", "2019-05-02", "result");
104         LOG.debug("the Qname is {} / namesapce {} ; module {}; ", pathQname.toString(), pathQname.getNamespace(),
105                 pathQname.getModule());
106         YangInstanceIdentifier yangId = YangInstanceIdentifier.of(pathQname);
107         DataObject dataObject = null;
108         //Create the object response
109         //Create JsonReader from String
110         InputStream streamGnpyRespnse = new ByteArrayInputStream(gnpyResponseString.getBytes(StandardCharsets.UTF_8));
111         InputStreamReader gnpyResultReader = new InputStreamReader(streamGnpyRespnse);
112         JsonReader jsonReader = new JsonReader(gnpyResultReader);
113         Optional<NormalizedNode<? extends PathArgument, ?>> transformIntoNormalizedNode = parseInputJSON(jsonReader,
114                 Result.class);
115         NormalizedNode<? extends PathArgument, ?> normalizedNode = transformIntoNormalizedNode.get();
116         if (codecRegistry.fromNormalizedNode(yangId, normalizedNode) != null) {
117             LOG.debug("The key of the generated object",
118                     codecRegistry.fromNormalizedNode(yangId, normalizedNode).getKey());
119             dataObject = codecRegistry.fromNormalizedNode(yangId, normalizedNode).getValue();
120         } else {
121             LOG.warn("The codec registry from the normalized node is null!");
122         }
123         List<Response> responses = null;
124         responses = ((Result) dataObject).getResponse();
125         if (responses != null) {
126             LOG.info("The response id is {}; ", responses.get(0).getResponseId());
127         } else {
128             LOG.warn("The response is null!");
129         }
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                 isFeasible = false;
139                 LOG.info("The path is not feasible ");
140             } else if (response.getResponseType() instanceof PathCase) {
141                 isFeasible = true;
142                 LOG.info("The path is feasible ");
143             }
144         }
145         return isFeasible;
146     }
147
148     public void analyzeResult() {
149         if (response != null) {
150             Long responseId = response.getResponseId();
151             LOG.info("Response-Id {}", responseId);
152             if (response.getResponseType() instanceof NoPathCase) {
153                 NoPathCase noPathCase = (NoPathCase) response.getResponseType();
154                 String noPathType = noPathCase.getNoPath().getNoPath();
155                 LOG.info("GNPy: No path - {}",noPathType);
156                 if (((noPathType == "NO_FEASIBLE_BAUDRATE_WITH_SPACING") && (noPathType == "NO_FEASIBLE_MODE"))
157                         && ((noPathType ==  "MODE_NOT_FEASIBLE") && (noPathType == "NO_SPECTRUM"))) {
158                     List<PathMetric> pathMetricList = noPathCase.getNoPath().getPathProperties().getPathMetric();
159                     LOG.info("GNPy : path is not feasible : {}", noPathType);
160                     for (PathMetric pathMetric : pathMetricList) {
161                         String metricType = pathMetric.getMetricType().getSimpleName();
162                         BigDecimal accumulativeValue = pathMetric.getAccumulativeValue();
163                         LOG.info("Metric type {} // AccumulatriveValue {}", metricType, accumulativeValue);
164                     }
165                 }
166             } else if (response.getResponseType() instanceof PathCase) {
167                 LOG.info("GNPy : path is feasible");
168                 PathCase pathCase = (PathCase) response.getResponseType();
169                 List<PathMetric> pathMetricList = pathCase.getPathProperties().getPathMetric();
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             }
176         }
177     }
178
179     public HardConstraints analyzeGnpyPath() {
180         HardConstraints hardConstraints = null;
181         if (response != null) {
182             Long responseId = response.getResponseId();
183             LOG.info("Response-Id {}", responseId);
184             if (response.getResponseType() instanceof NoPathCase) {
185                 NoPathCase noPathCase = (NoPathCase) response.getResponseType();
186                 LOG.info("No path feasible {}", noPathCase.toString());
187             } else if (response.getResponseType() instanceof PathCase) {
188                 PathCase pathCase = (PathCase) response.getResponseType();
189                 List<PathMetric> pathMetricList = pathCase.getPathProperties().getPathMetric();
190                 for (PathMetric pathMetric : pathMetricList) {
191                     String metricType = pathMetric.getMetricType().getSimpleName();
192                     BigDecimal accumulativeValue = pathMetric.getAccumulativeValue();
193                     LOG.info("Metric type {} // AccumulatriveValue {}", metricType, accumulativeValue);
194                 }
195
196                 // Includes the list of nodes in the GNPy computed path as constraints for the PCE
197                 List<OrderedHops> orderedHopsList = null;
198                 List<PathRouteObjects> pathRouteObjectList = pathCase.getPathProperties().getPathRouteObjects();
199                 int counter = 0;
200                 for (PathRouteObjects pathRouteObjects : pathRouteObjectList) {
201                     if (pathRouteObjects.getPathRouteObject().getType() instanceof NumUnnumHop) {
202                         NumUnnumHop numUnnumHop = (NumUnnumHop) pathRouteObjects.getPathRouteObject().getType();
203                         String nodeId = numUnnumHop.getNodeId();
204                         org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.routing.constraints.rev171017
205                                 .ordered.constraints.sp.hop.type.hop.type.Node node = new NodeBuilder()
206                                 .setNodeId(nodeId).build();
207                         HopType hopType = new HopTypeBuilder().setHopType(node).build();
208                         OrderedHops orderedHops = new OrderedHopsBuilder().setHopNumber(counter).setHopType(hopType)
209                                 .build();
210                         LOG.info("- gnpyResult class : Hard Constraint: {} // - Hop Node {}", counter, nodeId);
211                         orderedHopsList.add(orderedHops);
212                         counter++;
213                     }
214                 }
215                 Include include = new IncludeBuilder().setOrderedHops(orderedHopsList).build();
216                 General general = new GeneralBuilder().setInclude(include).build();
217                 hardConstraints = new HardConstraintsBuilder().setCoRoutingOrGeneral(general).build();
218             }
219         }
220         return hardConstraints;
221     }
222
223     /**
224      * Parses the input json with concrete implementation of
225      * {@link JsonParserStream}.
226      *
227      * @param reader
228      *            of the given JSON
229      * @throws Exception
230      *
231      */
232     private Optional<NormalizedNode<? extends YangInstanceIdentifier.PathArgument, ?>> parseInputJSON(JsonReader reader,
233             Class<? extends DataObject> objectClass) throws Exception {
234         NormalizedNodeResult result = new NormalizedNodeResult();
235         SchemaContext schemaContext = getSchemaContext(objectClass);
236         try (NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
237                 JsonParserStream jsonParser = JsonParserStream.create(streamWriter, schemaContext, schemaContext);) {
238             LOG.debug("GNPy: the path to the reader {}", reader.getPath());
239             LOG.debug("GNPy: the reader {}", reader.toString());
240             LOG.debug("GNPy: the jsonParser class {} // jsonParser to string {}", jsonParser.getClass(),
241                     jsonParser.toString());
242             jsonParser.parse(reader);
243         } catch (IOException e) {
244             LOG.warn("GNPy: exception {} occured during parsing Json input stream", e.getMessage());
245             return Optional.empty();
246         }
247         return Optional.ofNullable(result.getResult());
248     }
249
250     private SchemaContext getSchemaContext(Class<? extends DataObject> objectClass) throws Exception {
251         final ModuleInfoBackedContext moduleContext = ModuleInfoBackedContext.create();
252         Iterable<? extends YangModuleInfo> moduleInfos;
253         SchemaContext schemaContext = null;
254         moduleInfos = Collections.singleton(BindingReflections.getModuleInfo(objectClass));
255         moduleContext.addModuleInfos(moduleInfos);
256         schemaContext = moduleContext.tryToCreateSchemaContext().get();
257         return schemaContext;
258     }
259
260     private String readResultFromFile(String fileName) {
261         BufferedReader br = null;
262         FileReader fr = null;
263         StringBuilder sb = new StringBuilder();
264         String gnpyResponse;
265
266         try {
267             fr = new FileReader(fileName);
268             br = new BufferedReader(fr);
269             String currentLine;
270             while ((currentLine = br.readLine()) != null) {
271                 LOG.info(currentLine);
272                 sb.append(currentLine);
273             }
274             fr.close();
275         } catch (IOException e) {
276             LOG.warn("GNPy: exception {} occured during the reading of results", e.getMessage());
277         }
278         gnpyResponse = sb.toString();
279         return gnpyResponse;
280     }
281
282     /**
283      * Transforms the given input {@link NormalizedNode} into the given
284      * {@link DataObject}.
285      *
286      * @param normalizedNode
287      *            normalized node you want to convert
288      * @param rootNode
289      *            {@link QName} of converted normalized node root
290      *
291      *            <p>
292      *            The input object should be {@link ContainerNode}
293      *            </p>
294      */
295     public <T extends DataObject> Optional<T> getDataObject(@Nonnull NormalizedNode<?, ?> normalizedNode,
296             @Nonnull QName rootNode, BindingNormalizedNodeSerializer codecRegistry) {
297         if (normalizedNode != null) {
298             LOG.debug("GNPy: The codecRegistry is ", codecRegistry.toString());
299         } else {
300             LOG.warn("GNPy: The codecRegistry is null");
301         }
302         Preconditions.checkNotNull(normalizedNode);
303         if (normalizedNode instanceof ContainerNode) {
304             YangInstanceIdentifier.PathArgument directChildIdentifier = YangInstanceIdentifier.of(rootNode)
305                     .getLastPathArgument();
306             Optional<NormalizedNode<?, ?>> directChild = NormalizedNodes.getDirectChild(normalizedNode,
307                     directChildIdentifier);
308             if (!directChild.isPresent()) {
309                 throw new IllegalStateException(String.format("Could not get the direct child of %s", rootNode));
310             }
311             normalizedNode = directChild.get();
312             LOG.debug("GNPy: the normalized node is ", normalizedNode.getNodeType());
313         }
314         YangInstanceIdentifier rootNodeYangInstanceIdentifier = YangInstanceIdentifier.of(rootNode);
315         LOG.debug("GNPy: the root Node Yang Instance Identifier is ", rootNodeYangInstanceIdentifier.toString());
316         Map.Entry<?, ?> bindingNodeEntry = codecRegistry.fromNormalizedNode(rootNodeYangInstanceIdentifier,
317                 normalizedNode);
318         if (bindingNodeEntry == null) {
319             LOG.debug("The binding Node Entry is null");
320             return Optional.empty();
321         }
322         return Optional.ofNullable((T) bindingNodeEntry.getValue());
323     }
324 }