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