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