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