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