b051192ad21e54bcef1920f024a38ab371cdcda0
[transportpce.git] / renderer / src / main / java / org / opendaylight / transportpce / renderer / provisiondevice / RendererServiceOperationsImpl.java
1 /*
2  * Copyright © 2017 AT&T 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 package org.opendaylight.transportpce.renderer.provisiondevice;
9
10 import com.google.common.util.concurrent.Futures;
11 import com.google.common.util.concurrent.ListenableFuture;
12 import com.google.common.util.concurrent.ListeningExecutorService;
13 import com.google.common.util.concurrent.MoreExecutors;
14 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
15 import java.util.ArrayList;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Optional;
19 import java.util.Set;
20 import java.util.concurrent.Callable;
21 import java.util.concurrent.ExecutionException;
22 import java.util.concurrent.Executors;
23 import java.util.concurrent.TimeUnit;
24 import java.util.concurrent.TimeoutException;
25 import java.util.stream.Collectors;
26 import org.opendaylight.mdsal.binding.api.DataBroker;
27 import org.opendaylight.mdsal.binding.api.NotificationPublishService;
28 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
29 import org.opendaylight.transportpce.common.ResponseCodes;
30 import org.opendaylight.transportpce.common.StringConstants;
31 import org.opendaylight.transportpce.common.Timeouts;
32 import org.opendaylight.transportpce.common.mapping.PortMapping;
33 import org.opendaylight.transportpce.common.service.ServiceTypes;
34 import org.opendaylight.transportpce.renderer.ModelMappingUtils;
35 import org.opendaylight.transportpce.renderer.ServicePathInputData;
36 import org.opendaylight.transportpce.renderer.provisiondevice.servicepath.ServicePathDirection;
37 import org.opendaylight.transportpce.renderer.provisiondevice.tasks.DeviceRenderingRollbackTask;
38 import org.opendaylight.transportpce.renderer.provisiondevice.tasks.DeviceRenderingTask;
39 import org.opendaylight.transportpce.renderer.provisiondevice.tasks.OlmPowerSetupRollbackTask;
40 import org.opendaylight.transportpce.renderer.provisiondevice.tasks.OlmPowerSetupTask;
41 import org.opendaylight.transportpce.renderer.provisiondevice.tasks.OtnDeviceRenderingTask;
42 import org.opendaylight.transportpce.renderer.provisiondevice.tasks.RollbackProcessor;
43 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.device.renderer.rev211004.Action;
44 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.device.renderer.rev211004.OtnServicePathInput;
45 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.networkutils.rev220630.OtnLinkType;
46 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.olm.rev210618.GetPmInputBuilder;
47 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.olm.rev210618.GetPmOutput;
48 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.olm.rev210618.ServicePowerSetupInput;
49 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.olm.rev210618.ServicePowerTurndownInputBuilder;
50 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.olm.rev210618.ServicePowerTurndownOutput;
51 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.olm.rev210618.TransportpceOlmService;
52 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.olm.rev210618.get.pm.output.Measurements;
53 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev210915.RendererRpcResultSp;
54 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev210915.RendererRpcResultSpBuilder;
55 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev210915.ServiceDeleteInput;
56 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev210915.ServiceDeleteOutput;
57 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev210915.ServiceImplementationRequestInput;
58 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev210915.ServiceImplementationRequestOutput;
59 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev210915.link._for.notif.ATerminationBuilder;
60 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev210915.link._for.notif.ZTerminationBuilder;
61 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev210915.renderer.rpc.result.sp.Link;
62 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev210915.renderer.rpc.result.sp.LinkBuilder;
63 import org.opendaylight.yang.gen.v1.http.org.openroadm.device.types.rev191129.NodeTypes;
64 import org.opendaylight.yang.gen.v1.http.org.openroadm.resource.types.rev161014.ResourceTypeEnum;
65 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.format.rev191129.ServiceFormat;
66 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.service.list.Services;
67 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev230501.PathDescription;
68 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev220118.RpcStatusEx;
69 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev220118.ServicePathNotificationTypes;
70 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.servicepath.rev171017.ServicePathList;
71 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.servicepath.rev171017.service.path.list.ServicePaths;
72 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.servicepath.rev171017.service.path.list.ServicePathsKey;
73 import org.opendaylight.yang.gen.v1.http.org.transportpce.common.types.rev220926.PmGranularity;
74 import org.opendaylight.yang.gen.v1.http.org.transportpce.common.types.rev220926.link.tp.LinkTp;
75 import org.opendaylight.yang.gen.v1.http.org.transportpce.common.types.rev220926.olm.get.pm.input.ResourceIdentifierBuilder;
76 import org.opendaylight.yang.gen.v1.http.org.transportpce.common.types.rev220926.optical.renderer.nodes.Nodes;
77 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
78 import org.opendaylight.yangtools.yang.binding.Notification;
79 import org.opendaylight.yangtools.yang.common.Uint32;
80 import org.osgi.service.component.annotations.Activate;
81 import org.osgi.service.component.annotations.Component;
82 import org.osgi.service.component.annotations.Reference;
83 import org.slf4j.Logger;
84 import org.slf4j.LoggerFactory;
85
86
87 @Component(immediate = true)
88 public class RendererServiceOperationsImpl implements RendererServiceOperations {
89
90     private static final Logger LOG = LoggerFactory.getLogger(RendererServiceOperationsImpl.class);
91     private static final String DEVICE_RENDERING_ROLL_BACK_MSG =
92             "Device rendering was not successful! Rendering will be rolled back.";
93     private static final String OLM_ROLL_BACK_MSG =
94             "OLM power setup was not successful! Rendering and OLM will be rolled back.";
95     private static final String RENDERING_DEVICES_A_Z_MSG = "Rendering devices A-Z";
96     private static final String RENDERING_DEVICES_Z_A_MSG = "Rendering device Z-A";
97     private static final String TURNING_DOWN_POWER_ON_A_TO_Z_PATH_MSG = "Turning down power on A-to-Z path";
98     private static final String FAILED = "Failed";
99     private static final String OPERATION_FAILED = "Operation Failed";
100     private static final String OPERATION_SUCCESSFUL = "Operation Successful";
101     private static final int NUMBER_OF_THREADS = 4;
102
103     private final DeviceRendererService deviceRenderer;
104     private final OtnDeviceRendererService otnDeviceRenderer;
105     private final TransportpceOlmService olmService;
106     private final DataBroker dataBroker;
107     private final NotificationPublishService notificationPublishService;
108     private final PortMapping portMapping;
109     private ListeningExecutorService executor;
110
111     @Activate
112     public RendererServiceOperationsImpl(@Reference DeviceRendererService deviceRenderer,
113             @Reference OtnDeviceRendererService otnDeviceRenderer,
114             @Reference TransportpceOlmService olmService,
115             @Reference DataBroker dataBroker,
116             @Reference NotificationPublishService notificationPublishService,
117             @Reference PortMapping portMapping) {
118         this.deviceRenderer = deviceRenderer;
119         this.otnDeviceRenderer = otnDeviceRenderer;
120         this.olmService = olmService;
121         this.dataBroker = dataBroker;
122         this.notificationPublishService = notificationPublishService;
123         this.portMapping = portMapping;
124         this.executor = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(NUMBER_OF_THREADS));
125         LOG.debug("RendererServiceOperationsImpl instantiated");
126     }
127
128     @Override
129     public ListenableFuture<ServiceImplementationRequestOutput>
130             serviceImplementation(ServiceImplementationRequestInput input, boolean isTempService) {
131         LOG.info("Calling service impl request {}", input.getServiceName());
132         LOG.debug("Check if it is temp-service {}", isTempService);
133         return executor.submit(new Callable<ServiceImplementationRequestOutput>() {
134
135             @Override
136             public ServiceImplementationRequestOutput call() throws Exception {
137                 sendNotifications(
138                     ServicePathNotificationTypes.ServiceImplementationRequest,
139                     input.getServiceName(),
140                     RpcStatusEx.Pending,
141                     "Service compliant, submitting service implementation Request ...");
142                 Uint32 serviceRate = getServiceRate(input);
143                 LOG.info("Using {}G rate", serviceRate);
144                 org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.portmapping.rev231221
145                         .network.Nodes mappingNode =
146                     portMapping.isNodeExist(input.getServiceAEnd().getNodeId())
147                         ? portMapping.getNode(input.getServiceAEnd().getNodeId())
148                         : null;
149                 String serviceType = ServiceTypes.getServiceType(
150                     input.getServiceAEnd().getServiceFormat().getName(),
151                     serviceRate,
152                     mappingNode != null
153                         && NodeTypes.Xpdr.equals(mappingNode.getNodeInfo().getNodeType())
154                             && input.getServiceAEnd().getTxDirection() != null
155                             && input.getServiceAEnd().getTxDirection().getPort() != null
156                             && input.getServiceAEnd().getTxDirection().getPort().getPortName() != null
157                         ? portMapping.getMapping(input.getServiceAEnd().getNodeId(),
158                                 input.getServiceAEnd().getTxDirection().getPort().getPortName())
159                         : null);
160                 //TODO a Map might be more indicated here
161                 switch (serviceType) {
162                     case StringConstants.SERVICE_TYPE_100GE_T:
163                     case StringConstants.SERVICE_TYPE_400GE:
164                     case StringConstants.SERVICE_TYPE_OTU4:
165                     case StringConstants.SERVICE_TYPE_OTUC2:
166                     case StringConstants.SERVICE_TYPE_OTUC3:
167                     case StringConstants.SERVICE_TYPE_OTUC4:
168                         LOG.debug("Check temp service {}", isTempService);
169                         if (!manageServicePathCreation(input, serviceType, isTempService)) {
170                             return ModelMappingUtils
171                                 .createServiceImplResponse(ResponseCodes.RESPONSE_FAILED, OPERATION_FAILED);
172                         }
173                         break;
174                     case StringConstants.SERVICE_TYPE_1GE:
175                     case StringConstants.SERVICE_TYPE_10GE:
176                     case StringConstants.SERVICE_TYPE_100GE_M:
177                     case StringConstants.SERVICE_TYPE_100GE_S:
178                     case StringConstants.SERVICE_TYPE_ODU4:
179                     case StringConstants.SERVICE_TYPE_ODUC2:
180                     case StringConstants.SERVICE_TYPE_ODUC3:
181                     case StringConstants.SERVICE_TYPE_ODUC4:
182                         if (!manageOtnServicePathCreation(input, serviceType, serviceRate)) {
183                             return ModelMappingUtils
184                                 .createServiceImplResponse(ResponseCodes.RESPONSE_FAILED, OPERATION_FAILED);
185                         }
186                         break;
187                     default:
188                         LOG.error("unsupported service-type");
189                         return ModelMappingUtils
190                             .createServiceImplResponse(ResponseCodes.RESPONSE_FAILED, OPERATION_FAILED);
191                 }
192                 return ModelMappingUtils
193                     .createServiceImplResponse(ResponseCodes.RESPONSE_OK, OPERATION_SUCCESSFUL);
194             }
195         });
196     }
197
198     @Override
199     public ListenableFuture<ServiceDeleteOutput> serviceDelete(ServiceDeleteInput input, Services service) {
200         String serviceName = input.getServiceName();
201         LOG.info("Calling service delete request {}", serviceName);
202         return executor.submit(new Callable<ServiceDeleteOutput>() {
203
204             @Override
205             public ServiceDeleteOutput call() throws Exception {
206                 sendNotifications(
207                     ServicePathNotificationTypes.ServiceDelete,
208                     serviceName,
209                     RpcStatusEx.Pending,
210                     "Service compliant, submitting service delete Request ...");
211                 // Obtain path description
212                 Optional<
213                     org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev220118
214                         .service.path.PathDescription> pathDescriptionOpt =
215                     getPathDescriptionFromDatastore(serviceName);
216                 if (pathDescriptionOpt.isEmpty()) {
217                     LOG.error("Unable to get path description for service {}!", serviceName);
218                     sendNotifications(
219                         ServicePathNotificationTypes.ServiceDelete,
220                         serviceName,
221                         RpcStatusEx.Failed,
222                         "Unable to get path description for service");
223                     return ModelMappingUtils
224                         .createServiceDeleteResponse(ResponseCodes.RESPONSE_FAILED, OPERATION_FAILED);
225                 }
226                 PathDescription pathDescription = pathDescriptionOpt.orElseThrow();
227                 String serviceType =
228                     ServiceTypes.getServiceType(
229                         service.getServiceAEnd().getServiceFormat().getName(),
230                         service.getServiceAEnd().getServiceRate(),
231                         service.getServiceAEnd().getTxDirection() == null
232                                 || service.getServiceAEnd().getTxDirection().values().stream().findFirst().orElseThrow()
233                                     .getPort() == null
234                                 || service.getServiceAEnd().getTxDirection().values().stream().findFirst().orElseThrow()
235                                     .getPort().getPortName() == null
236                             ? null
237                             : portMapping.getMapping(
238                                     service.getServiceAEnd().getNodeId().getValue(),
239                                     service.getServiceAEnd().getTxDirection().values().stream().findFirst()
240                                         .orElseThrow().getPort().getPortName()));
241                 switch (serviceType) {
242                     case StringConstants.SERVICE_TYPE_100GE_T:
243                     case StringConstants.SERVICE_TYPE_400GE:
244                     case StringConstants.SERVICE_TYPE_OTU4:
245                     case StringConstants.SERVICE_TYPE_OTUC2:
246                     case StringConstants.SERVICE_TYPE_OTUC3:
247                     case StringConstants.SERVICE_TYPE_OTUC4:
248                         if (!manageServicePathDeletion(serviceName, pathDescription, serviceType)) {
249                             return ModelMappingUtils
250                                 .createServiceDeleteResponse(ResponseCodes.RESPONSE_FAILED, OPERATION_FAILED);
251                         }
252                         break;
253                     case StringConstants.SERVICE_TYPE_1GE:
254                     case StringConstants.SERVICE_TYPE_10GE:
255                     case StringConstants.SERVICE_TYPE_100GE_M:
256                     case StringConstants.SERVICE_TYPE_100GE_S:
257                     case StringConstants.SERVICE_TYPE_ODU4:
258                     case StringConstants.SERVICE_TYPE_ODUC2:
259                     case StringConstants.SERVICE_TYPE_ODUC3:
260                     case StringConstants.SERVICE_TYPE_ODUC4:
261                         if (!manageOtnServicePathDeletion(serviceName, pathDescription, service, serviceType)) {
262                             return ModelMappingUtils
263                                 .createServiceDeleteResponse(ResponseCodes.RESPONSE_FAILED, OPERATION_FAILED);
264                         }
265                         break;
266                     default:
267                         LOG.error("unsupported service-type");
268                         return ModelMappingUtils
269                             .createServiceDeleteResponse(ResponseCodes.RESPONSE_FAILED, OPERATION_FAILED);
270                 }
271                 return ModelMappingUtils
272                     .createServiceDeleteResponse(ResponseCodes.RESPONSE_OK, OPERATION_SUCCESSFUL);
273             }
274         });
275     }
276
277     @SuppressFBWarnings(
278         value = "UPM_UNCALLED_PRIVATE_METHOD",
279         justification = "call in call() method")
280     private Uint32 getServiceRate(ServiceImplementationRequestInput input) {
281         if (input.getServiceAEnd() == null) {
282             LOG.warn("Unable to get service-rate for service {}", input.getServiceName());
283             return Uint32.ZERO;
284         }
285         if (input.getServiceAEnd().getServiceRate() != null) {
286             return input.getServiceAEnd().getServiceRate();
287         }
288         LOG.warn("Input should have rate if you are using 200 or 300G");
289         // TODO: missing 200, and 300G rates here, OTUCn cannot always be 400G
290         Map<ServiceFormat, Map<String, Uint32>> formatRateMap  = Map.of(
291                 ServiceFormat.OTU, Map.of(
292                     "OTUCn", Uint32.valueOf(400),
293                     "OTU4", Uint32.valueOf(100),
294                     "OTU2", Uint32.valueOf(10),
295                     "OTU2e", Uint32.valueOf(10)),
296                 ServiceFormat.ODU, Map.of(
297                     "ODUCn",Uint32.valueOf(400),
298                     "ODU4", Uint32.valueOf(100),
299                     "ODU2", Uint32.valueOf(10),
300                     "ODU2e", Uint32.valueOf(10),
301                     "ODU0", Uint32.valueOf(1)));
302         if (!formatRateMap.containsKey(input.getServiceAEnd().getServiceFormat())) {
303             LOG.warn("Unable to get service-rate for service {} - unsupported service format {}",
304                 input.getServiceName(), input.getServiceAEnd().getServiceFormat());
305             return Uint32.ZERO;
306         }
307         String serviceName =
308             ServiceFormat.OTU.getName().equals(input.getServiceAEnd().getServiceFormat().getName())
309                 ? input.getServiceAEnd().getOtuServiceRate().toString().split("\\{")[0]
310                 : input.getServiceAEnd().getOduServiceRate().toString().split("\\{")[0];
311         if (!formatRateMap.get(input.getServiceAEnd().getServiceFormat()).containsKey(serviceName)) {
312             LOG.warn("Unable to get service-rate for service {} - unsupported service name {}",
313                 input.getServiceName(), serviceName);
314             return Uint32.ZERO;
315         }
316         return formatRateMap
317             .get(input.getServiceAEnd().getServiceFormat())
318             .get(serviceName);
319     }
320
321     @SuppressFBWarnings(
322             value = "UPM_UNCALLED_PRIVATE_METHOD",
323             justification = "call in call() method")
324     private ServicePowerTurndownOutput olmPowerTurndown(ServicePathInputData servicePathInputData)
325             throws InterruptedException, ExecutionException, TimeoutException {
326         LOG.debug(TURNING_DOWN_POWER_ON_A_TO_Z_PATH_MSG);
327         return this.olmService
328             .servicePowerTurndown(
329                 new ServicePowerTurndownInputBuilder(servicePathInputData.getServicePathInput()).build())
330             .get(Timeouts.DATASTORE_READ, TimeUnit.MILLISECONDS)
331             .getResult();
332     }
333
334     @SuppressFBWarnings(
335             value = "UPM_UNCALLED_PRIVATE_METHOD",
336             justification = "call in call() method")
337     private Optional<org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev220118
338             .service.path.PathDescription> getPathDescriptionFromDatastore(String serviceName) {
339         InstanceIdentifier<org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev220118
340                 .service.path.PathDescription> pathDescriptionIID =
341             InstanceIdentifier.create(ServicePathList.class)
342                 .child(ServicePaths.class, new ServicePathsKey(serviceName))
343                 .child(org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev220118
344                     .service.path.PathDescription.class);
345         try {
346             LOG.debug("Getting path description for service {}", serviceName);
347             return this.dataBroker.newReadOnlyTransaction()
348                     .read(LogicalDatastoreType.OPERATIONAL, pathDescriptionIID)
349                     .get(Timeouts.DATASTORE_READ, TimeUnit.MILLISECONDS);
350         } catch (InterruptedException | ExecutionException | TimeoutException e) {
351             LOG.warn("Exception while getting path description from datastore {} for service {}!",
352                     pathDescriptionIID, serviceName, e);
353             return Optional.empty();
354         }
355     }
356
357     @SuppressFBWarnings(
358             value = "UPM_UNCALLED_PRIVATE_METHOD",
359             justification = "call in call() method")
360     private List<DeviceRenderingResult> deviceRendering(
361             RollbackProcessor rollbackProcessor,
362             ServicePathInputData servicePathDataAtoZ,
363             ServicePathInputData servicePathDataZtoA) {
364
365         //TODO atozrenderingFuture & ztoarenderingFuture & renderingCombinedFuture used only once
366         //     Do notifications & LOG.info deserve this ?
367         LOG.info(RENDERING_DEVICES_A_Z_MSG);
368         sendNotifications(
369             ServicePathNotificationTypes.ServiceImplementationRequest,
370             servicePathDataAtoZ.getServicePathInput().getServiceName(),
371             RpcStatusEx.Pending,
372             RENDERING_DEVICES_A_Z_MSG);
373         ListenableFuture<DeviceRenderingResult> atozrenderingFuture =
374             this.executor.submit(
375                 new DeviceRenderingTask(this.deviceRenderer, servicePathDataAtoZ, ServicePathDirection.A_TO_Z));
376
377         LOG.info(RENDERING_DEVICES_Z_A_MSG);
378         sendNotifications(
379             ServicePathNotificationTypes.ServiceImplementationRequest,
380             servicePathDataZtoA.getServicePathInput().getServiceName(),
381             RpcStatusEx.Pending,
382             RENDERING_DEVICES_Z_A_MSG);
383         ListenableFuture<DeviceRenderingResult> ztoarenderingFuture =
384             this.executor.submit(
385                 new DeviceRenderingTask(this.deviceRenderer, servicePathDataZtoA, ServicePathDirection.Z_TO_A));
386
387         ListenableFuture<List<DeviceRenderingResult>> renderingCombinedFuture =
388             Futures.allAsList(atozrenderingFuture, ztoarenderingFuture);
389
390         List<DeviceRenderingResult> renderingResults = new ArrayList<>(2);
391         try {
392             LOG.info("Waiting for A-Z and Z-A device renderers ...");
393             renderingResults = renderingCombinedFuture.get(Timeouts.RENDERING_TIMEOUT, TimeUnit.MILLISECONDS);
394         } catch (InterruptedException | ExecutionException | TimeoutException e) {
395             LOG.warn(DEVICE_RENDERING_ROLL_BACK_MSG, e);
396             sendNotifications(
397                 ServicePathNotificationTypes.ServiceImplementationRequest,
398                 servicePathDataAtoZ.getServicePathInput().getServiceName(),
399                 RpcStatusEx.Pending,
400                 DEVICE_RENDERING_ROLL_BACK_MSG);
401             //FIXME we can't do rollback here, because we don't have rendering results.
402             return renderingResults;
403         }
404
405         rollbackProcessor.addTask(
406             new DeviceRenderingRollbackTask(
407                 "AtoZDeviceTask",
408                 ! renderingResults.get(0).isSuccess(),
409                 renderingResults.get(0).getRenderedNodeInterfaces(),
410                 this.deviceRenderer));
411         rollbackProcessor.addTask(
412                 new DeviceRenderingRollbackTask("ZtoADeviceTask",
413                 ! renderingResults.get(1).isSuccess(),
414                 renderingResults.get(1).getRenderedNodeInterfaces(),
415                 this.deviceRenderer));
416         return renderingResults;
417     }
418
419     @SuppressFBWarnings(
420         value = "UPM_UNCALLED_PRIVATE_METHOD",
421         justification = "call in call() method")
422     private List<OtnDeviceRenderingResult> otnDeviceRendering(
423             RollbackProcessor rollbackProcessor,
424             OtnServicePathInput otnServicePathAtoZ,
425             OtnServicePathInput otnServicePathZtoA,
426             String serviceType) {
427
428         //TODO atozrenderingFuture & ztoarenderingFuture & renderingCombinedFuture used only once
429         //     Do notifications & LOG.info deserve this ?
430         LOG.info(RENDERING_DEVICES_A_Z_MSG);
431         sendNotifications(
432             ServicePathNotificationTypes.ServiceImplementationRequest,
433             otnServicePathAtoZ.getServiceName(),
434             RpcStatusEx.Pending,
435             RENDERING_DEVICES_A_Z_MSG);
436         ListenableFuture<OtnDeviceRenderingResult> atozrenderingFuture =
437             this.executor.submit(
438                 new OtnDeviceRenderingTask(this.otnDeviceRenderer, otnServicePathAtoZ, serviceType));
439
440         LOG.info(RENDERING_DEVICES_Z_A_MSG);
441         sendNotifications(
442             ServicePathNotificationTypes.ServiceImplementationRequest,
443             otnServicePathZtoA.getServiceName(),
444             RpcStatusEx.Pending,
445             RENDERING_DEVICES_Z_A_MSG);
446         ListenableFuture<OtnDeviceRenderingResult> ztoarenderingFuture =
447             this.executor.submit(
448                 new OtnDeviceRenderingTask(this.otnDeviceRenderer, otnServicePathZtoA, serviceType));
449
450         ListenableFuture<List<OtnDeviceRenderingResult>> renderingCombinedFuture =
451             Futures.allAsList(atozrenderingFuture, ztoarenderingFuture);
452         List<OtnDeviceRenderingResult> otnRenderingResults = new ArrayList<>(2);
453         try {
454             LOG.info("Waiting for A-Z and Z-A device renderers ...");
455             otnRenderingResults = renderingCombinedFuture.get(Timeouts.RENDERING_TIMEOUT, TimeUnit.MILLISECONDS);
456         } catch (InterruptedException | ExecutionException | TimeoutException e) {
457             LOG.warn(DEVICE_RENDERING_ROLL_BACK_MSG, e);
458             sendNotifications(
459                 ServicePathNotificationTypes.ServiceImplementationRequest,
460                 otnServicePathAtoZ.getServiceName(),
461                 RpcStatusEx.Pending,
462                 DEVICE_RENDERING_ROLL_BACK_MSG);
463             //FIXME we can't do rollback here, because we don't have rendering results.
464             return otnRenderingResults;
465         }
466         for (int i = 0; i < otnRenderingResults.size(); i++) {
467             rollbackProcessor.addTask(
468                 new DeviceRenderingRollbackTask(
469                     "DeviceTask n° " + i + 1,
470                     ! otnRenderingResults.get(i).isSuccess(),
471                     otnRenderingResults.get(i).getRenderedNodeInterfaces(),
472                     this.deviceRenderer));
473         }
474         return otnRenderingResults;
475     }
476
477     @SuppressFBWarnings(
478             value = "UPM_UNCALLED_PRIVATE_METHOD",
479             justification = "call in call() method")
480     private void olmPowerSetup(
481             RollbackProcessor rollbackProcessor,
482             ServicePowerSetupInput powerSetupInputAtoZ,
483             ServicePowerSetupInput powerSetupInputZtoA, boolean isTempService) {
484
485         //TODO olmPowerSetupFutureAtoZ & olmPowerSetupFutureZtoA & olmFutures used only once
486         //     Do notifications & LOG.info deserve this ?
487         //TODO use constants for LOG.info & notifications common messages
488         // if the service create is a temp-service, OLM will be skipped
489         if (isTempService) {
490             LOG.info("For temp-service create OLM is not computed and skipped");
491             return;
492         }
493         LOG.info("Olm power setup A-Z");
494         sendNotifications(
495                 ServicePathNotificationTypes.ServiceImplementationRequest,
496                 powerSetupInputAtoZ.getServiceName(),
497                 RpcStatusEx.Pending,
498                 "Olm power setup A-Z");
499         ListenableFuture<OLMRenderingResult> olmPowerSetupFutureAtoZ =
500                 this.executor.submit(new OlmPowerSetupTask(this.olmService, powerSetupInputAtoZ));
501
502         LOG.info("OLM power setup Z-A");
503         sendNotifications(
504                 ServicePathNotificationTypes.ServiceImplementationRequest,
505                 powerSetupInputAtoZ.getServiceName(),
506                 RpcStatusEx.Pending,
507                 "Olm power setup Z-A");
508         ListenableFuture<OLMRenderingResult> olmPowerSetupFutureZtoA =
509                 this.executor.submit(new OlmPowerSetupTask(this.olmService, powerSetupInputZtoA));
510         ListenableFuture<List<OLMRenderingResult>> olmFutures =
511                 Futures.allAsList(olmPowerSetupFutureAtoZ, olmPowerSetupFutureZtoA);
512
513         List<OLMRenderingResult> olmResults;
514         try {
515             LOG.info("Waiting for A-Z and Z-A OLM power setup ...");
516             olmResults = olmFutures.get(Timeouts.OLM_TIMEOUT, TimeUnit.MILLISECONDS);
517         } catch (InterruptedException | ExecutionException | TimeoutException e) {
518             LOG.warn(OLM_ROLL_BACK_MSG, e);
519             sendNotifications(
520                     ServicePathNotificationTypes.ServiceImplementationRequest,
521                     powerSetupInputAtoZ.getServiceName(),
522                     RpcStatusEx.Pending,
523                     OLM_ROLL_BACK_MSG);
524             rollbackProcessor.addTask(
525                     new OlmPowerSetupRollbackTask("AtoZOLMTask", true, this.olmService, powerSetupInputAtoZ));
526             rollbackProcessor.addTask(
527                     new OlmPowerSetupRollbackTask("ZtoAOLMTask", true, this.olmService, powerSetupInputZtoA));
528             return;
529         }
530         rollbackProcessor.addTask(
531                 new OlmPowerSetupRollbackTask(
532                         "AtoZOLMTask",
533                         !olmResults.get(0).isSuccess(),
534                         this.olmService,
535                         powerSetupInputAtoZ));
536         rollbackProcessor.addTask(
537                 new OlmPowerSetupRollbackTask(
538                         "ZtoAOLMTask",
539                         !olmResults.get(1).isSuccess(),
540                         this.olmService,
541                         powerSetupInputZtoA));
542     }
543
544     @SuppressFBWarnings(
545             value = "UPM_UNCALLED_PRIVATE_METHOD",
546             justification = "call in call() method")
547     private boolean isServiceActivated(String nodeId, String tpId) {
548         LOG.info("Starting service activation test on node {} and tp {}", nodeId, tpId);
549         for (int i = 0; i < 3; i++) {
550             List<Measurements> measurements = getMeasurements(nodeId, tpId);
551             if (measurements == null) {
552                 LOG.warn("Device {} is not reporting PreFEC on TP: {}", nodeId, tpId);
553                 return true;
554             }
555             if (verifyPreFecBer(measurements)) {
556                 return true;
557             }
558             try {
559                 Thread.sleep(Timeouts.SERVICE_ACTIVATION_TEST_RETRY_TIME);
560             } catch (InterruptedException ex) {
561                 Thread.currentThread().interrupt();
562             }
563         }
564         LOG.error("Service activation test failed on node {} and termination point {}!", nodeId, tpId);
565         return false;
566     }
567
568     private List<Measurements> getMeasurements(String nodeId, String tp) {
569         try {
570             GetPmOutput getPmOutput =
571                 this.olmService
572                     .getPm(
573                         new GetPmInputBuilder()
574                             .setNodeId(nodeId)
575                             .setGranularity(PmGranularity._15min)
576                             .setResourceIdentifier(new ResourceIdentifierBuilder().setResourceName(tp + "-OTU").build())
577                             .setResourceType(ResourceTypeEnum.Interface)
578                             .build())
579                     .get()
580                     .getResult();
581             if ((getPmOutput == null) || (getPmOutput.getNodeId() == null)) {
582                 LOG.warn("OLM's get PM failed for node {} and tp {}", nodeId, tp);
583             } else {
584                 LOG.info("successfully finished calling OLM's get PM");
585                 return getPmOutput.getMeasurements();
586                 // may return null
587             }
588
589         } catch (ExecutionException | InterruptedException e) {
590             LOG.warn("Error occurred while getting PM for node {} and tp {}", nodeId, tp, e);
591         }
592         return null;
593     }
594
595     private boolean verifyPreFecBer(List<Measurements> measurements) {
596         double preFecCorrectedErrors = Double.MIN_VALUE;
597         double fecUncorrectableBlocks = Double.MIN_VALUE;
598
599         for (Measurements measurement : measurements) {
600             switch (measurement.getPmparameterName()) {
601                 case "preFECCorrectedErrors":
602                     preFecCorrectedErrors = Double.parseDouble(measurement.getPmparameterValue());
603                     break;
604                 case "FECUncorrectableBlocks":
605                     fecUncorrectableBlocks = Double.parseDouble(measurement.getPmparameterValue());
606                     break;
607                 default:
608                     break;
609             }
610         }
611
612         LOG.info("Measurements: preFECCorrectedErrors = {}; FECUncorrectableBlocks = {}",
613                 preFecCorrectedErrors, fecUncorrectableBlocks);
614
615         if (fecUncorrectableBlocks > Double.MIN_VALUE) {
616             LOG.error("Data has uncorrectable errors, BER test failed");
617             return false;
618         }
619
620         double numOfBitsPerSecond = 112000000000d;
621         double threshold = 0.00002d;
622         double result = preFecCorrectedErrors / numOfBitsPerSecond;
623         LOG.info("PreFEC value is {}", Double.toString(result));
624         return result <= threshold;
625     }
626
627     @SuppressFBWarnings(
628         value = "UPM_UNCALLED_PRIVATE_METHOD",
629         justification = "call in call() method")
630     private boolean manageServicePathCreation(ServiceImplementationRequestInput input, String serviceType,
631                                               boolean isTempService) {
632         ServicePathInputData servicePathInputDataAtoZ =
633             ModelMappingUtils
634                 .rendererCreateServiceInputAToZ(input.getServiceName(), input.getPathDescription(), Action.Create);
635         ServicePathInputData servicePathInputDataZtoA =
636             ModelMappingUtils
637                 .rendererCreateServiceInputZToA(input.getServiceName(), input.getPathDescription(), Action.Create);
638         // Rollback should be same for all conditions, so creating a new one
639         RollbackProcessor rollbackProcessor = new RollbackProcessor();
640         List<DeviceRenderingResult> renderingResults =
641             deviceRendering(rollbackProcessor, servicePathInputDataAtoZ, servicePathInputDataZtoA);
642         if (rollbackProcessor.rollbackAllIfNecessary() > 0 || renderingResults.isEmpty()) {
643             sendNotifications(
644                 ServicePathNotificationTypes.ServiceImplementationRequest,
645                 input.getServiceName(),
646                 RpcStatusEx.Failed,
647                 DEVICE_RENDERING_ROLL_BACK_MSG);
648             return false;
649         }
650         olmPowerSetup(
651             rollbackProcessor,
652             //olmPowerSetupInputAtoZ,
653             ModelMappingUtils.createServicePowerSetupInput(renderingResults.get(0).getOlmList(), input),
654             //olmPowerSetupInputZtoA
655             ModelMappingUtils.createServicePowerSetupInput(renderingResults.get(1).getOlmList(), input), isTempService);
656         if (rollbackProcessor.rollbackAllIfNecessary() > 0) {
657             sendNotifications(
658                 ServicePathNotificationTypes.ServiceImplementationRequest,
659                 input.getServiceName(),
660                 RpcStatusEx.Failed,
661                 OLM_ROLL_BACK_MSG);
662             return false;
663         }
664         // run service activation test twice - once on source node and once on
665         // destination node
666         List<Nodes> nodes = servicePathInputDataAtoZ.getServicePathInput().getNodes();
667         if ((nodes == null) || (nodes.isEmpty())) {
668             return false;
669         }
670
671         Nodes sourceNode = nodes.get(0);
672         Nodes destNode = nodes.get(nodes.size() - 1);
673         String srcNetworkTp =
674             sourceNode.getDestTp().contains(StringConstants.NETWORK_TOKEN)
675                 ? sourceNode.getDestTp()
676                 : sourceNode.getSrcTp();
677         String dstNetowrkTp =
678             destNode.getDestTp().contains(StringConstants.NETWORK_TOKEN)
679                 ? destNode.getDestTp()
680                 : destNode.getSrcTp();
681
682         if (!isServiceActivated(sourceNode.getNodeId(), srcNetworkTp)
683                 || !isServiceActivated(destNode.getNodeId(), dstNetowrkTp)) {
684             rollbackProcessor.rollbackAll();
685             sendNotifications(
686                 ServicePathNotificationTypes.ServiceImplementationRequest,
687                 input.getServiceName(),
688                 RpcStatusEx.Failed,
689                 "Service activation test failed.");
690             return false;
691         }
692         sendNotificationsWithPathDescription(
693             ServicePathNotificationTypes.ServiceImplementationRequest,
694             input.getServiceName(),
695             RpcStatusEx.Successful,
696             OPERATION_SUCCESSFUL,
697             input.getPathDescription(),
698             createLinkForNotif(
699                 renderingResults.stream()
700                     .flatMap(rr -> rr.getOtnLinkTps().stream())
701                     .collect(Collectors.toList())),
702             null,
703             serviceType);
704         return true;
705     }
706
707     @SuppressFBWarnings(
708         value = "UPM_UNCALLED_PRIVATE_METHOD",
709         justification = "call in call() method")
710     private boolean manageServicePathDeletion(String serviceName, PathDescription pathDescription, String serviceType)
711             throws InterruptedException {
712         ServicePathInputData servicePathInputDataAtoZ =
713             ModelMappingUtils.rendererCreateServiceInputAToZ(serviceName, pathDescription, Action.Delete);
714         ServicePathInputData servicePathInputDataZtoA =
715             ModelMappingUtils.rendererCreateServiceInputZToA(serviceName, pathDescription, Action.Delete);
716         // OLM turn down power
717         try {
718             LOG.debug(TURNING_DOWN_POWER_ON_A_TO_Z_PATH_MSG);
719             sendNotifications(
720                 ServicePathNotificationTypes.ServiceDelete,
721                 serviceName,
722                 RpcStatusEx.Pending,
723                 TURNING_DOWN_POWER_ON_A_TO_Z_PATH_MSG);
724             // TODO add some flag rather than string
725             if (FAILED.equals(
726                     olmPowerTurndown(servicePathInputDataAtoZ)
727                         .getResult())) {
728                 LOG.error("Service power turndown failed on A-to-Z path for service {}!", serviceName);
729                 sendNotifications(
730                     ServicePathNotificationTypes.ServiceDelete,
731                     serviceName,
732                     RpcStatusEx.Failed,
733                     "Service power turndown failed on A-to-Z path for service");
734                 return false;
735             }
736             LOG.debug("Turning down power on Z-to-A path");
737             sendNotifications(
738                 ServicePathNotificationTypes.ServiceDelete,
739                 serviceName,
740                 RpcStatusEx.Pending,
741                 "Turning down power on Z-to-A path");
742             // TODO add some flag rather than string
743             if (FAILED.equals(
744                     olmPowerTurndown(servicePathInputDataZtoA)
745                         .getResult())) {
746                 LOG.error("Service power turndown failed on Z-to-A path for service {}!", serviceName);
747                 sendNotifications(
748                     ServicePathNotificationTypes.ServiceDelete,
749                     serviceName,
750                     RpcStatusEx.Failed,
751                     "Service power turndown failed on Z-to-A path for service");
752                 return false;
753             }
754         } catch (InterruptedException | ExecutionException | TimeoutException e) {
755             LOG.error("Error while turning down power!", e);
756             return false;
757         }
758         // delete service path with renderer
759         LOG.info("Deleting service path via renderer");
760         sendNotifications(
761             ServicePathNotificationTypes.ServiceDelete,
762             serviceName,
763             RpcStatusEx.Pending,
764             "Deleting service path via renderer");
765         sendNotificationsWithPathDescription(
766             ServicePathNotificationTypes.ServiceDelete,
767             serviceName,
768             RpcStatusEx.Successful,
769             OPERATION_SUCCESSFUL,
770             pathDescription,
771             createLinkForNotif(
772                 deviceRendering(
773                         new RollbackProcessor(),
774                         servicePathInputDataAtoZ,
775                         servicePathInputDataZtoA)
776                     .stream()
777                     .flatMap(rr -> rr.getOtnLinkTps().stream())
778                     .collect(Collectors.toList())),
779             null,
780             serviceType);
781         return true;
782     }
783
784     @SuppressFBWarnings(
785         value = "UPM_UNCALLED_PRIVATE_METHOD",
786         justification = "call in call() method")
787     private boolean manageOtnServicePathCreation(
788             ServiceImplementationRequestInput input,
789             String serviceType,
790             Uint32 serviceRate) {
791         // Rollback should be same for all conditions, so creating a new one
792         RollbackProcessor rollbackProcessor = new RollbackProcessor();
793         List<OtnDeviceRenderingResult> renderingResults =
794             otnDeviceRendering(
795                 rollbackProcessor,
796                 // This is A-Z side
797                 ModelMappingUtils
798                     .rendererCreateOtnServiceInput(
799                         input.getServiceName(),
800                         Action.Create,
801                         input.getServiceAEnd().getServiceFormat().getName(),
802                         serviceRate,
803                         input.getPathDescription(),
804                         true),
805                 // This is Z-A side
806                 ModelMappingUtils
807                     .rendererCreateOtnServiceInput(
808                         input.getServiceName(),
809                         Action.Create,
810                         input.getServiceZEnd().getServiceFormat().getName(),
811                         serviceRate,
812                         input.getPathDescription(),
813                         false),
814                 serviceType);
815         if (rollbackProcessor.rollbackAllIfNecessary() > 0) {
816             rollbackProcessor.rollbackAll();
817             sendNotifications(
818                 ServicePathNotificationTypes.ServiceImplementationRequest,
819                 input.getServiceName(),
820                 RpcStatusEx.Failed,
821                 DEVICE_RENDERING_ROLL_BACK_MSG);
822             return false;
823         }
824         sendNotificationsWithPathDescription(
825             ServicePathNotificationTypes.ServiceImplementationRequest,
826             input.getServiceName(),
827             RpcStatusEx.Successful, OPERATION_SUCCESSFUL,
828             input.getPathDescription(),
829             createLinkForNotif(
830                 renderingResults.stream()
831                     .flatMap(rr -> rr.getOtnLinkTps().stream())
832                     .collect(Collectors.toList())),
833             getSupportedLinks(
834                 ModelMappingUtils.getLinksFromServicePathDescription(input.getPathDescription()),
835                 serviceType),
836             serviceType);
837         return true;
838     }
839
840     @SuppressFBWarnings(
841         value = "UPM_UNCALLED_PRIVATE_METHOD",
842         justification = "call in call() method")
843     private boolean manageOtnServicePathDeletion(
844             String serviceName,
845             PathDescription pathDescription,
846             Services service,
847             String serviceType) {
848         LOG.info("Deleting otn-service path {} via renderer", serviceName);
849         sendNotifications(
850                 ServicePathNotificationTypes.ServiceDelete,
851                 serviceName,
852                 RpcStatusEx.Pending,
853                 "Deleting otn-service path via renderer");
854         List<OtnDeviceRenderingResult> renderingResults =
855             otnDeviceRendering(
856                 new RollbackProcessor(),
857                 // This is A-Z side
858                 ModelMappingUtils
859                     .rendererCreateOtnServiceInput(
860                         serviceName,
861                         Action.Delete,
862                         service.getServiceAEnd().getServiceFormat().getName(),
863                         service.getServiceAEnd().getServiceRate(),
864                         pathDescription,
865                         true),
866                 // This is Z-A side
867                 ModelMappingUtils
868                     .rendererCreateOtnServiceInput(
869                         serviceName,
870                         Action.Delete,
871                         service.getServiceZEnd().getServiceFormat().getName(),
872                         service.getServiceAEnd().getServiceRate(),
873                         pathDescription,
874                         false),
875                 serviceType);
876         sendNotificationsWithPathDescription(
877             ServicePathNotificationTypes.ServiceDelete,
878             serviceName,
879             RpcStatusEx.Successful,
880             OPERATION_SUCCESSFUL,
881             pathDescription,
882             createLinkForNotif(
883                 renderingResults.stream()
884                     .flatMap(rr -> rr.getOtnLinkTps().stream())
885                     .collect(Collectors.toList())),
886             getSupportedLinks(
887                 ModelMappingUtils.getLinksFromServicePathDescription(pathDescription),
888                 serviceType),
889             serviceType);
890         return true;
891     }
892
893     /**
894      * Send renderer notification.
895      * @param servicePathNotificationTypes ServicePathNotificationTypes
896      * @param serviceName String
897      * @param rpcStatusEx RpcStatusEx
898      * @param message String
899      */
900     private void sendNotifications(
901             ServicePathNotificationTypes servicePathNotificationTypes,
902             String serviceName,
903             RpcStatusEx rpcStatusEx,
904             String message) {
905         send(
906             buildNotification(servicePathNotificationTypes, serviceName, rpcStatusEx, message,
907                 null, null, null, null));
908     }
909
910     /**
911      * Send renderer notification with path description information.
912      * @param servicePathNotificationTypes ServicePathNotificationTypes
913      * @param serviceName String
914      * @param rpcStatusEx RpcStatusEx
915      * @param message String
916      * @param pathDescription PathDescription
917      */
918     private void sendNotificationsWithPathDescription(
919             ServicePathNotificationTypes servicePathNotificationTypes,
920             String serviceName,
921             RpcStatusEx rpcStatusEx,
922             String message,
923             PathDescription pathDescription,
924             Link notifLink,
925             Set<String> supportedLinks,
926             String serviceType) {
927         send(
928             buildNotification(servicePathNotificationTypes, serviceName, rpcStatusEx, message,
929                 pathDescription, notifLink, supportedLinks, serviceType));
930     }
931
932     /**
933      * Build notification containing path description information.
934      * @param servicePathNotificationTypes ServicePathNotificationTypes
935      * @param serviceName String
936      * @param rpcStatusEx RpcStatusEx
937      * @param message String
938      * @param pathDescription PathDescription
939      * @return notification with RendererRpcResultSp type.
940      */
941     private RendererRpcResultSp buildNotification(
942             ServicePathNotificationTypes servicePathNotificationTypes,
943             String serviceName,
944             RpcStatusEx rpcStatusEx,
945             String message,
946             PathDescription pathDescription,
947             Link notifLink,
948             Set<String> supportedLinks,
949             String serviceType) {
950         RendererRpcResultSpBuilder builder =
951             new RendererRpcResultSpBuilder()
952                 .setNotificationType(servicePathNotificationTypes).setServiceName(serviceName).setStatus(rpcStatusEx)
953                 .setStatusMessage(message)
954                 .setServiceType(serviceType);
955         if (pathDescription != null) {
956             builder
957                 .setAToZDirection(pathDescription.getAToZDirection())
958                 .setZToADirection(pathDescription.getZToADirection());
959         }
960         if (notifLink != null) {
961             builder.setLink(notifLink);
962         }
963         if (supportedLinks != null) {
964             builder.setLinkId(supportedLinks);
965         }
966         return builder.build();
967     }
968
969     /**
970      * Send renderer notification.
971      * @param notification Notification
972      */
973     private void send(Notification<?> notification) {
974         try {
975             LOG.info("Sending notification {}", notification);
976             notificationPublishService.putNotification(notification);
977         } catch (InterruptedException e) {
978             LOG.info("notification offer rejected: ", e);
979             Thread.currentThread().interrupt();
980         }
981     }
982
983     private Link createLinkForNotif(List<LinkTp> otnLinkTerminationPoints) {
984         return
985             otnLinkTerminationPoints == null || otnLinkTerminationPoints.size() != 2
986                 ? null
987                 : new LinkBuilder()
988                     .setATermination(
989                         new ATerminationBuilder()
990                             .setNodeId(otnLinkTerminationPoints.get(0).getNodeId())
991                             .setTpId(otnLinkTerminationPoints.get(0).getTpId())
992                             .build())
993                     .setZTermination(
994                         new ZTerminationBuilder()
995                             .setNodeId(otnLinkTerminationPoints.get(1).getNodeId())
996                             .setTpId(otnLinkTerminationPoints.get(1).getTpId())
997                             .build())
998                     .build();
999     }
1000
1001     private Set<String> getSupportedLinks(Set<String> allSupportLinks, String serviceType) {
1002         //TODO a Map might be more indicated here
1003         switch (serviceType) {
1004             case StringConstants.SERVICE_TYPE_10GE:
1005             case StringConstants.SERVICE_TYPE_1GE:
1006                 return allSupportLinks.stream()
1007                     .filter(lk -> lk.startsWith(OtnLinkType.ODTU4.getName())).collect(Collectors.toSet());
1008             case StringConstants.SERVICE_TYPE_100GE_M:
1009                 return allSupportLinks.stream()
1010                     .filter(lk -> lk.startsWith(OtnLinkType.ODUC4.getName())).collect(Collectors.toSet());
1011             case StringConstants.SERVICE_TYPE_ODU4:
1012             case StringConstants.SERVICE_TYPE_100GE_S:
1013                 return allSupportLinks.stream()
1014                     .filter(lk -> lk.startsWith(OtnLinkType.OTU4.getName())).collect(Collectors.toSet());
1015             case StringConstants.SERVICE_TYPE_ODUC4:
1016                 return allSupportLinks.stream()
1017                     .filter(lk -> lk.startsWith(OtnLinkType.OTUC4.getName())).collect(Collectors.toSet());
1018             default:
1019                 return null;
1020         }
1021     }
1022 }