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