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