Migrate PCE module to Aluminium
[transportpce.git] / pce / src / main / java / org / opendaylight / transportpce / pce / gnpy / GnpyServiceImpl.java
1 /*
2  * Copyright © 2019 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 java.math.BigDecimal;
12 import java.nio.charset.StandardCharsets;
13 import java.util.ArrayList;
14 import java.util.HashMap;
15 import java.util.Iterator;
16 import java.util.List;
17 import java.util.Map;
18 import org.eclipse.jdt.annotation.NonNull;
19 import org.eclipse.jdt.annotation.Nullable;
20 import org.opendaylight.transportpce.pce.constraints.PceConstraints;
21 import org.opendaylight.transportpce.pce.constraints.PceConstraints.ResourcePair;
22 import org.opendaylight.yang.gen.v1.gnpy.gnpy.network.topology.rev181214.topo.Elements;
23 import org.opendaylight.yang.gen.v1.gnpy.gnpy.network.topology.rev181214.topo.ElementsKey;
24 import org.opendaylight.yang.gen.v1.gnpy.path.rev200202.RouteIncludeEro;
25 import org.opendaylight.yang.gen.v1.gnpy.path.rev200202.TeHopType;
26 import org.opendaylight.yang.gen.v1.gnpy.path.rev200202.TeNodeId;
27 import org.opendaylight.yang.gen.v1.gnpy.path.rev200202.TePathDisjointness;
28 import org.opendaylight.yang.gen.v1.gnpy.path.rev200202.TeTpId;
29 import org.opendaylight.yang.gen.v1.gnpy.path.rev200202.common.constraints_config.TeBandwidth;
30 import org.opendaylight.yang.gen.v1.gnpy.path.rev200202.common.constraints_config.TeBandwidthBuilder;
31 import org.opendaylight.yang.gen.v1.gnpy.path.rev200202.explicit.route.hop.Type;
32 import org.opendaylight.yang.gen.v1.gnpy.path.rev200202.explicit.route.hop.type.NumUnnumHopBuilder;
33 import org.opendaylight.yang.gen.v1.gnpy.path.rev200202.explicit.route.hop.type.num.unnum.hop.NumUnnumHop;
34 import org.opendaylight.yang.gen.v1.gnpy.path.rev200202.generic.path.constraints.PathConstraints;
35 import org.opendaylight.yang.gen.v1.gnpy.path.rev200202.generic.path.constraints.PathConstraintsBuilder;
36 import org.opendaylight.yang.gen.v1.gnpy.path.rev200202.gnpy.specific.parameters.EffectiveFreqSlot;
37 import org.opendaylight.yang.gen.v1.gnpy.path.rev200202.gnpy.specific.parameters.EffectiveFreqSlotBuilder;
38 import org.opendaylight.yang.gen.v1.gnpy.path.rev200202.path.route.objects.ExplicitRouteObjects;
39 import org.opendaylight.yang.gen.v1.gnpy.path.rev200202.path.route.objects.ExplicitRouteObjectsBuilder;
40 import org.opendaylight.yang.gen.v1.gnpy.path.rev200202.path.route.objects.explicit.route.objects.RouteObjectIncludeExclude;
41 import org.opendaylight.yang.gen.v1.gnpy.path.rev200202.path.route.objects.explicit.route.objects.RouteObjectIncludeExcludeBuilder;
42 import org.opendaylight.yang.gen.v1.gnpy.path.rev200202.path.route.objects.explicit.route.objects.RouteObjectIncludeExcludeKey;
43 import org.opendaylight.yang.gen.v1.gnpy.path.rev200202.service.PathRequest;
44 import org.opendaylight.yang.gen.v1.gnpy.path.rev200202.service.PathRequestBuilder;
45 import org.opendaylight.yang.gen.v1.gnpy.path.rev200202.service.PathRequestKey;
46 import org.opendaylight.yang.gen.v1.gnpy.path.rev200202.synchronization.info.Synchronization;
47 import org.opendaylight.yang.gen.v1.gnpy.path.rev200202.synchronization.info.SynchronizationBuilder;
48 import org.opendaylight.yang.gen.v1.gnpy.path.rev200202.synchronization.info.synchronization.Svec;
49 import org.opendaylight.yang.gen.v1.gnpy.path.rev200202.synchronization.info.synchronization.SvecBuilder;
50 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev200128.PathComputationRequestInput;
51 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev200629.path.description.AToZDirection;
52 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev200629.path.description.ZToADirection;
53 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev200629.path.description.atoz.direction.AToZ;
54 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev200629.path.description.ztoa.direction.ZToA;
55 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev200629.pce.resource.resource.Resource;
56 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
57 import org.opendaylight.yangtools.yang.common.Uint32;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
60
61 /**
62  * Class to create the service corresponding to GNPy requirements.
63  *
64  * @author Ahmed Triki ( ahmed.triki@orange.com )
65  *
66  */
67
68 public class GnpyServiceImpl {
69     private static final Logger LOG = LoggerFactory.getLogger(GnpyServiceImpl.class);
70   //Fix-grid channel width (THz)
71     private static final double FIX_CH = 0.05;
72   //Number of slot in 50GHz channel (THz)
73     private static final int NB_SLOT_BW = 4;
74   //Nominal central frequency granularity (THz)
75     private static final double SLOT_BW = 0.00625;
76   //Minimum channel center frequency (openRoadm spec) (THz)
77     private static final double MAX_CENTRAL_FREQ = 196.1;
78   //Flex-grid reference channel frequency (THz)
79     private static final double FLEX_CENTRAL_FREQ = 193.1;
80   //Convert THz to Hz
81     private static final double CONVERT_TH_HZ = 1e12;
82
83     private Map<PathRequestKey, PathRequest> pathRequest = new HashMap<>();
84     private List<Synchronization> synchronization = new ArrayList<>();
85     private Map<String, String> mapDisgNodeRefNode = new HashMap<>();
86     private Map<String, IpAddress> mapNodeRefIp = new HashMap<>();
87     private Map<String, List<String>> mapLinkFiber = new HashMap<>();
88     private Map<String, IpAddress> mapFiberIp = new HashMap<>();
89     private List<String> trxList = new ArrayList<>();
90     private Map<ElementsKey, Elements> elements = new HashMap<>();
91     private Map<RouteObjectIncludeExcludeKey,RouteObjectIncludeExclude> routeObjectIncludeExcludes = new HashMap<>();
92     private IpAddress currentNodeIpAddress = null;
93
94     /*
95      * Construct the GnpyServiceImpl
96      */
97     public GnpyServiceImpl(PathComputationRequestInput input, AToZDirection atoz, Uint32 requestId,
98                 GnpyTopoImpl gnpyTopo, PceConstraints pceHardConstraints) throws GnpyException {
99         this.elements = gnpyTopo.getElements();
100         this.mapDisgNodeRefNode = gnpyTopo.getMapDisgNodeRefNode();
101         this.mapNodeRefIp = gnpyTopo.getMapNodeRefIp();
102         this.mapLinkFiber = gnpyTopo.getMapLinkFiber();
103         this.mapFiberIp = gnpyTopo.getMapFiberIp();
104         this.trxList = gnpyTopo.getTrxList();
105         try {
106             this.pathRequest = extractPathRequest(input, atoz, requestId.toJava(), pceHardConstraints);
107             this.synchronization = extractSynchronization(requestId);
108         } catch (NullPointerException e) {
109             throw new GnpyException("In GnpyServiceImpl: one of the elements is null",e);
110         }
111     }
112
113     public GnpyServiceImpl(PathComputationRequestInput input, ZToADirection ztoa, Uint32 requestId,
114                 GnpyTopoImpl gnpyTopo, PceConstraints pceHardConstraints) throws GnpyException {
115         this.elements = gnpyTopo.getElements();
116         this.mapDisgNodeRefNode = gnpyTopo.getMapDisgNodeRefNode();
117         this.mapNodeRefIp = gnpyTopo.getMapNodeRefIp();
118         this.mapLinkFiber = gnpyTopo.getMapLinkFiber();
119         this.mapFiberIp = gnpyTopo.getMapFiberIp();
120         this.trxList = gnpyTopo.getTrxList();
121         try {
122             pathRequest = extractPathRequest(input, ztoa, requestId.toJava(), pceHardConstraints);
123             synchronization = extractSynchronization(requestId);
124         } catch (NullPointerException e) {
125             throw new GnpyException("In GnpyServiceImpl: one of the elements of service is null",e);
126         }
127     }
128
129     private Map<PathRequestKey, PathRequest> extractPathRequest(
130             PathComputationRequestInput input, AToZDirection atoz, Long requestId,
131             PceConstraints pceHardConstraints) throws GnpyException {
132
133         // Create the source and destination nodes
134         String sourceNode = input.getServiceAEnd().getNodeId();
135         String destNode = input.getServiceZEnd().getNodeId();
136         if (!trxList.contains(sourceNode) || !trxList.contains(destNode)) {
137             throw new GnpyException("In GnpyServiceImpl: source and destination should be transmitter nodes");
138         }
139
140         // Create explicitRouteObjects
141         List<AToZ> listAtoZ = new ArrayList<>(atoz.nonnullAToZ().values());
142         if (!listAtoZ.isEmpty()) {
143             extractRouteObjectIcludeAtoZ(listAtoZ);
144         } else {
145             extractHardConstraints(pceHardConstraints);
146         }
147         ExplicitRouteObjects explicitRouteObjects = new ExplicitRouteObjectsBuilder()
148             .setRouteObjectIncludeExclude(routeObjectIncludeExcludes).build();
149         //Create Path Constraint
150         Long atozWavelength = null;
151         if (atoz.getAToZWavelengthNumber() != null) {
152             atozWavelength = atoz.getAToZWavelengthNumber().toJava();
153         }
154         PathConstraints pathConstraints = createPathConstraints(atoz.getRate().toJava(), atozWavelength);
155
156         // Create the path request
157         Map<PathRequestKey, PathRequest> pathRequestMap = new HashMap<>();
158         PathRequest pathRequestEl = new PathRequestBuilder().setRequestId(Uint32.valueOf(requestId))
159             .setSource(this.mapNodeRefIp.get(sourceNode)).setDestination(this.mapNodeRefIp.get(destNode))
160             .setSrcTpId("srcTpId".getBytes(StandardCharsets.UTF_8))
161             .setDstTpId("dstTpId".getBytes(StandardCharsets.UTF_8))
162             .setBidirectional(false).setPathConstraints(pathConstraints).setPathConstraints(pathConstraints)
163             .setExplicitRouteObjects(explicitRouteObjects).build();
164         pathRequestMap.put(pathRequestEl.key(),pathRequestEl);
165         LOG.debug("In GnpyServiceImpl: path request AToZ is extracted");
166         return pathRequestMap;
167     }
168
169     private Map<PathRequestKey, PathRequest> extractPathRequest(
170             PathComputationRequestInput input, ZToADirection ztoa, Long requestId,
171             PceConstraints pceHardConstraints) throws GnpyException {
172         // Create the source and destination nodes
173         String sourceNode = input.getServiceZEnd().getNodeId();
174         String destNode = input.getServiceAEnd().getNodeId();
175         if (!trxList.contains(sourceNode) || !trxList.contains(destNode)) {
176             throw new GnpyException("In GnpyServiceImpl: source and destination should be transmitter nodes");
177         }
178         // Create explicitRouteObjects
179         @NonNull List<ZToA> listZtoA = new ArrayList<>(ztoa.nonnullZToA().values());
180         if (!listZtoA.isEmpty()) {
181             extractRouteObjectIcludeZtoA(listZtoA);
182         } else {
183             extractHardConstraints(pceHardConstraints);
184         }
185         ExplicitRouteObjects explicitRouteObjects = new ExplicitRouteObjectsBuilder()
186             .setRouteObjectIncludeExclude(routeObjectIncludeExcludes).build();
187         //Create Path Constraint
188         Long ztoaWavelength = null;
189         if (ztoa.getZToAWavelengthNumber() != null) {
190             ztoaWavelength = ztoa.getZToAWavelengthNumber().toJava();
191         }
192         PathConstraints pathConstraints = createPathConstraints(ztoa.getRate().toJava(), ztoaWavelength);
193
194         // Create the path request
195         Map<PathRequestKey, PathRequest> pathRequestMap = new HashMap<>();
196         PathRequest pathRequestEl = new PathRequestBuilder().setRequestId(Uint32.valueOf(requestId))
197             .setSource(this.mapNodeRefIp.get(sourceNode)).setDestination(this.mapNodeRefIp.get(destNode))
198             .setSrcTpId("srcTpId".getBytes(StandardCharsets.UTF_8))
199             .setDstTpId("dstTpId".getBytes(StandardCharsets.UTF_8))
200             .setBidirectional(false).setPathConstraints(pathConstraints)
201             .setExplicitRouteObjects(explicitRouteObjects).build();
202         pathRequestMap.put(pathRequestEl.key(),pathRequestEl);
203         LOG.debug("In GnpyServiceImpl: path request ZToA is extracted");
204         return pathRequestMap;
205     }
206
207     //Extract RouteObjectIncludeExclude list in the case of pre-computed path A-to-Z
208     private void extractRouteObjectIcludeAtoZ(List<AToZ> listAtoZ) throws GnpyException {
209         Long index = 0L;
210         for (AToZ entry : listAtoZ) {
211             index = createResource(entry.getResource().getResource(),index);
212         }
213     }
214
215     //Extract RouteObjectIncludeExclude list in the case of pre-computed path Z-to-A
216     private void extractRouteObjectIcludeZtoA(@NonNull List<ZToA> listZtoA) throws GnpyException {
217         Long index = 0L;
218         for (ZToA entry : listZtoA) {
219             index = createResource(entry.getResource().getResource(),index);
220         }
221     }
222
223     //Create a new resource node or link
224     private Long createResource(@Nullable Resource resource, Long index) throws GnpyException {
225         Long idx = index;
226         if (resource
227             instanceof
228                 org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev200629
229                     .pce.resource.resource.resource.Node) {
230             org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev200629
231                 .pce.resource.resource.resource.Node node =
232                 (org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev200629
233                     .pce.resource.resource.resource.Node) resource;
234             if (node.getNodeId() == null) {
235                 throw new GnpyException("In gnpyServiceImpl: nodeId is null");
236             }
237             idx = addNodeToRouteObject(this.mapDisgNodeRefNode.get(node.getNodeId()),idx);
238         }
239
240         if (resource
241             instanceof
242                 org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev200629
243                     .pce.resource.resource.resource.Link) {
244             org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev200629
245                 .pce.resource.resource.resource.Link link =
246                 (org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev200629
247                     .pce.resource.resource.resource.Link) resource;
248             idx = addLinkToRouteObject(link.getLinkId(),idx);
249         }
250         return idx;
251     }
252
253     //Create RouteObjectIncludeExclude list in the case of hard constraint
254     private void extractHardConstraints(PceConstraints pceHardConstraints) throws GnpyException {
255         List<String> listNodeToInclude = getListToInclude(pceHardConstraints);
256         if (!listNodeToInclude.isEmpty()) {
257             Long index = 0L;
258             for (int i = 0; i < listNodeToInclude.size(); i++) {
259                 String nodeId = listNodeToInclude.get(i);
260                 index = addNodeToRouteObject(nodeId, index);
261             }
262         }
263     }
264
265     // Create the list of nodes to include
266     private List<String> getListToInclude(PceConstraints pceHardConstraints) {
267         List<String> listNodeToInclude = new ArrayList<>();
268         if (pceHardConstraints != null) {
269             List<ResourcePair> listToInclude = pceHardConstraints.getListToInclude();
270             Iterator<ResourcePair> it = listToInclude.iterator();
271             while (it.hasNext()) {
272                 ResourcePair rs = it.next();
273                 if (rs.getType().name().equals("NODE")) {
274                     listNodeToInclude.add(rs.getName());
275                 }
276             }
277         }
278         return listNodeToInclude;
279     }
280
281     //Add a node to the route object
282     private Long addNodeToRouteObject(String nodeRef, Long index) throws GnpyException {
283         Long idx = index;
284         IpAddress ipAddress = this.mapNodeRefIp.get(nodeRef);
285         if (ipAddress == null) {
286             throw new GnpyException(String.format("In gnpyServiceImpl : NodeRef %s does not exist", nodeRef));
287         }
288
289         for (Elements element : this.elements.values()) {
290             if (element.getUid().contains(ipAddress.getIpv4Address().getValue())) {
291                 if ((this.currentNodeIpAddress == null) || (!this.currentNodeIpAddress.equals(ipAddress))) {
292                     this.currentNodeIpAddress = ipAddress;
293                     RouteObjectIncludeExclude routeObjectIncludeExclude =
294                         addRouteObjectIncludeExclude(ipAddress, Uint32.valueOf(1),idx);
295                     routeObjectIncludeExcludes.put(routeObjectIncludeExclude.key(),routeObjectIncludeExclude);
296                     idx += 1;
297                 }
298                 return idx;
299             }
300         }
301         throw new GnpyException(String.format("In gnpyServiceImpl : NodeRef %s does not exist",nodeRef));
302     }
303
304     //Add a link to the route object
305     private Long addLinkToRouteObject(String linkId, Long index) throws GnpyException {
306         Long idx = index;
307         if (linkId == null) {
308             throw new GnpyException("In GnpyServiceImpl: the linkId is null");
309         }
310         //Only the ROADM-to-ROADM link are included in the route object
311         if (!mapLinkFiber.containsKey(linkId)) {
312             return idx;
313         }
314         List<String> listSubLink = this.mapLinkFiber.get(linkId);
315         if (listSubLink == null) {
316             throw new GnpyException(String.format("In gnpyServiceImpl addNodeRouteObject : no sublink in %s",linkId));
317         }
318         for (String subLink : listSubLink) {
319             IpAddress fiberIp = this.mapFiberIp.get(subLink);
320             if (fiberIp == null) {
321                 throw new GnpyException(String.format("In gnpyServiceImpl addNodeRouteObject : fiberIp of %s is null",
322                     subLink));
323             }
324             RouteObjectIncludeExclude routeObjectIncludeExclude =
325                 addRouteObjectIncludeExclude(fiberIp, Uint32.valueOf(1),idx);
326             routeObjectIncludeExcludes.put(routeObjectIncludeExclude.key(),routeObjectIncludeExclude);
327             idx += 1;
328         }
329         return idx;
330     }
331
332     // Add routeObjectIncludeExclude
333     private RouteObjectIncludeExclude addRouteObjectIncludeExclude(IpAddress ipAddress, Uint32 teTpValue, Long index) {
334         TeNodeId teNodeId = new TeNodeId(ipAddress);
335         TeTpId teTpId = new TeTpId(teTpValue);
336         NumUnnumHop numUnnumHop = new org.opendaylight.yang.gen.v1.gnpy.path.rev200202.explicit.route.hop.type.num
337             .unnum.hop.NumUnnumHopBuilder()
338                 .setNodeId(teNodeId.getIpv4Address().getValue())
339                 .setLinkTpId(teTpId.getUint32().toString())
340                 .setHopType(TeHopType.STRICT).build();
341         Type type1 = new NumUnnumHopBuilder().setNumUnnumHop(numUnnumHop).build();
342         // Create routeObjectIncludeExclude element
343         return new RouteObjectIncludeExcludeBuilder()
344             .setIndex(Uint32.valueOf(index)).setExplicitRouteUsage(RouteIncludeEro.class).setType(type1).build();
345     }
346
347     //Create the path constraints
348     private PathConstraints createPathConstraints(Long rate, Long wavelengthNumber) {
349         // Create EffectiveFreqSlot
350         int freqNdex = 0;
351         if (wavelengthNumber != null) {
352             double freq = (MAX_CENTRAL_FREQ - FIX_CH * (wavelengthNumber - 1));
353             freqNdex = (int) Math.round((freq - FLEX_CENTRAL_FREQ) / SLOT_BW);
354         }
355         EffectiveFreqSlot effectiveFreqSlot1 = new EffectiveFreqSlotBuilder().setM(NB_SLOT_BW).setN(freqNdex).build();
356         // Create Te-Bandwidth
357         TeBandwidth teBandwidth = new TeBandwidthBuilder().setPathBandwidth(new BigDecimal(rate))
358             .setTechnology("flexi-grid").setTrxType("openroadm-beta1")
359             .setTrxMode("W100G").setEffectiveFreqSlot(Map.of(effectiveFreqSlot1.key(),effectiveFreqSlot1))
360             .setSpacing(BigDecimal.valueOf(FIX_CH * CONVERT_TH_HZ)).build();
361         return new PathConstraintsBuilder().setTeBandwidth(teBandwidth).build();
362     }
363
364     //Create the synchronization
365     private List<Synchronization> extractSynchronization(Uint32 requestId) {
366         // Create RequestIdNumber
367         List<Uint32> requestIdNumber = new ArrayList<>();
368         requestIdNumber.add(requestId);
369         // Create a synchronization
370         Svec svec = new SvecBuilder().setRelaxable(true)
371             .setDisjointness(new TePathDisjointness(true, true, false))
372             .setRequestIdNumber(requestIdNumber).build();
373         List<Synchronization> synchro = new ArrayList<>();
374         Synchronization synchronization1 = new SynchronizationBuilder().setSynchronizationId(Uint32.valueOf(0))
375                 .setSvec(svec).build();
376         synchro.add(synchronization1);
377         return (synchro);
378     }
379
380     public Map<PathRequestKey, PathRequest> getPathRequest() {
381         return pathRequest;
382     }
383
384     public void setPathRequest(Map<PathRequestKey, PathRequest> pathRequest) {
385         this.pathRequest = pathRequest;
386     }
387
388     public List<Synchronization> getSynchronization() {
389         return synchronization;
390     }
391
392     public void setSynchronization(List<Synchronization> synchronization) {
393         this.synchronization = synchronization;
394     }
395
396 }