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