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