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