8b07c40097d80ef713e9ee6ee41446567faf6e60
[transportpce.git] / renderer / src / main / java / org / opendaylight / transportpce / renderer / provisiondevice / RendererServiceOperationsImpl.java
1 /*
2  * Copyright © 2017 AT&T and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.transportpce.renderer.provisiondevice;
9
10 import com.google.common.util.concurrent.Futures;
11 import com.google.common.util.concurrent.ListenableFuture;
12 import com.google.common.util.concurrent.ListeningExecutorService;
13 import com.google.common.util.concurrent.MoreExecutors;
14 import java.util.ArrayList;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Optional;
18 import java.util.concurrent.Callable;
19 import java.util.concurrent.ExecutionException;
20 import java.util.concurrent.Executors;
21 import java.util.concurrent.Future;
22 import java.util.concurrent.TimeUnit;
23 import java.util.concurrent.TimeoutException;
24 import org.opendaylight.mdsal.binding.api.DataBroker;
25 import org.opendaylight.mdsal.binding.api.NotificationPublishService;
26 import org.opendaylight.mdsal.binding.api.ReadTransaction;
27 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
28 import org.opendaylight.transportpce.common.ResponseCodes;
29 import org.opendaylight.transportpce.common.StringConstants;
30 import org.opendaylight.transportpce.common.Timeouts;
31 import org.opendaylight.transportpce.common.mapping.PortMapping;
32 import org.opendaylight.transportpce.common.service.ServiceTypes;
33 import org.opendaylight.transportpce.renderer.ModelMappingUtils;
34 import org.opendaylight.transportpce.renderer.ServicePathInputData;
35 import org.opendaylight.transportpce.renderer.provisiondevice.servicepath.ServicePathDirection;
36 import org.opendaylight.transportpce.renderer.provisiondevice.tasks.DeviceRenderingRollbackTask;
37 import org.opendaylight.transportpce.renderer.provisiondevice.tasks.DeviceRenderingTask;
38 import org.opendaylight.transportpce.renderer.provisiondevice.tasks.OlmPowerSetupRollbackTask;
39 import org.opendaylight.transportpce.renderer.provisiondevice.tasks.OlmPowerSetupTask;
40 import org.opendaylight.transportpce.renderer.provisiondevice.tasks.OtnDeviceRenderingTask;
41 import org.opendaylight.transportpce.renderer.provisiondevice.tasks.RollbackProcessor;
42 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.device.renderer.rev210618.Action;
43 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.device.renderer.rev210618.OtnServicePathInput;
44 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.olm.rev210618.GetPmInputBuilder;
45 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.olm.rev210618.GetPmOutput;
46 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.olm.rev210618.ServicePowerSetupInput;
47 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.olm.rev210618.ServicePowerTurndownInputBuilder;
48 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.olm.rev210618.ServicePowerTurndownOutput;
49 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.olm.rev210618.TransportpceOlmService;
50 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.olm.rev210618.get.pm.output.Measurements;
51 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev210618.RendererRpcResultSp;
52 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev210618.RendererRpcResultSpBuilder;
53 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev210618.ServiceDeleteInput;
54 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev210618.ServiceDeleteOutput;
55 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev210618.ServiceImplementationRequestInput;
56 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev210618.ServiceImplementationRequestOutput;
57 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev210618.link._for.notif.ATerminationBuilder;
58 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev210618.link._for.notif.ZTerminationBuilder;
59 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev210618.renderer.rpc.result.sp.Link;
60 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev210618.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.pm.types.rev161014.PmGranularity;
63 import org.opendaylight.yang.gen.v1.http.org.openroadm.resource.types.rev161014.ResourceTypeEnum;
64 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.format.rev190531.ServiceFormat;
65 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev190531.service.list.Services;
66 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev210705.PathDescription;
67 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev200128.RpcStatusEx;
68 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev200128.ServicePathNotificationTypes;
69 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.servicepath.rev171017.ServicePathList;
70 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.servicepath.rev171017.service.path.list.ServicePaths;
71 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.servicepath.rev171017.service.path.list.ServicePathsKey;
72 import org.opendaylight.yang.gen.v1.http.org.transportpce.common.types.rev210618.link.tp.LinkTp;
73 import org.opendaylight.yang.gen.v1.http.org.transportpce.common.types.rev210618.olm.get.pm.input.ResourceIdentifierBuilder;
74 import org.opendaylight.yang.gen.v1.http.org.transportpce.common.types.rev210618.optical.renderer.nodes.Nodes;
75 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
76 import org.opendaylight.yangtools.yang.binding.Notification;
77 import org.opendaylight.yangtools.yang.common.RpcResult;
78 import org.opendaylight.yangtools.yang.common.Uint32;
79 import org.slf4j.Logger;
80 import org.slf4j.LoggerFactory;
81
82
83 public class RendererServiceOperationsImpl implements RendererServiceOperations {
84
85     private static final String DEVICE_RENDERING_ROLL_BACK_MSG =
86             "Device rendering was not successful! Rendering will be rolled back.";
87     private static final String OLM_ROLL_BACK_MSG =
88             "OLM power setup was not successful! Rendering and OLM will be rolled back.";
89     private static final String RENDERING_DEVICES_A_Z_MSG = "Rendering devices A-Z";
90     private static final String RENDERING_DEVICES_Z_A_MSG = "Rendering device Z-A";
91     private static final String TURNING_DOWN_POWER_ON_A_TO_Z_PATH_MSG = "Turning down power on A-to-Z path";
92     private static final Logger LOG = LoggerFactory.getLogger(RendererServiceOperationsImpl.class);
93     private static final String FAILED = "Failed";
94     private static final String OPERATION_FAILED = "Operation Failed";
95     private static final String OPERATION_SUCCESSFUL = "Operation Successful";
96     private static final int NUMBER_OF_THREADS = 4;
97
98     private final DeviceRendererService deviceRenderer;
99     private final OtnDeviceRendererService otnDeviceRenderer;
100     private final TransportpceOlmService olmService;
101     private final DataBroker dataBroker;
102     private final NotificationPublishService notificationPublishService;
103     private final PortMapping portMapping;
104     private ListeningExecutorService executor;
105
106     public RendererServiceOperationsImpl(DeviceRendererService deviceRenderer,
107             OtnDeviceRendererService otnDeviceRenderer, TransportpceOlmService olmService,
108             DataBroker dataBroker, NotificationPublishService notificationPublishService, PortMapping portMapping) {
109         this.deviceRenderer = deviceRenderer;
110         this.otnDeviceRenderer = otnDeviceRenderer;
111         this.olmService = olmService;
112         this.dataBroker = dataBroker;
113         this.notificationPublishService = notificationPublishService;
114         this.portMapping = portMapping;
115         this.executor = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(NUMBER_OF_THREADS));
116     }
117
118     @Override
119     public ListenableFuture<ServiceImplementationRequestOutput>
120             serviceImplementation(ServiceImplementationRequestInput input) {
121         LOG.info("Calling service impl request {}", input.getServiceName());
122         return executor.submit(new Callable<ServiceImplementationRequestOutput>() {
123
124             @Override
125             public ServiceImplementationRequestOutput call() throws Exception {
126                 sendNotifications(ServicePathNotificationTypes.ServiceImplementationRequest, input.getServiceName(),
127                         RpcStatusEx.Pending, "Service compliant, submitting service implementation Request ...");
128                 Uint32 serviceRate = getServiceRate(input);
129                 String serviceType = ServiceTypes.getServiceType(
130                     input.getServiceAEnd().getServiceFormat().getName(),
131                     serviceRate,
132                     (NodeTypes.Xpdr.equals(portMapping.getNode(input.getServiceAEnd().getNodeId())
133                         .getNodeInfo().getNodeType())
134                             && input.getServiceAEnd().getTxDirection() != null
135                             && input.getServiceAEnd().getTxDirection().getPort() != null
136                             && input.getServiceAEnd().getTxDirection().getPort().getPortName() != null)
137                         ? portMapping.getMapping(input.getServiceAEnd().getNodeId(),
138                                 input.getServiceAEnd().getTxDirection().getPort().getPortName())
139                         : null);
140
141                 switch (serviceType) {
142                     case StringConstants.SERVICE_TYPE_100GE_T:
143                     case StringConstants.SERVICE_TYPE_400GE:
144                     case StringConstants.SERVICE_TYPE_OTU4:
145                     case StringConstants.SERVICE_TYPE_OTUC4:
146                         if (!manageServicePathCreation(input, serviceType)) {
147                             return ModelMappingUtils.createServiceImplResponse(ResponseCodes.RESPONSE_FAILED,
148                                 OPERATION_FAILED);
149                         }
150                         break;
151                     case StringConstants.SERVICE_TYPE_1GE:
152                     case StringConstants.SERVICE_TYPE_10GE:
153                     case StringConstants.SERVICE_TYPE_100GE_M:
154                     case StringConstants.SERVICE_TYPE_ODU4:
155                     case StringConstants.SERVICE_TYPE_ODUC4:
156                         if (!manageOtnServicePathCreation(input, serviceType, serviceRate)) {
157                             return ModelMappingUtils.createServiceImplResponse(ResponseCodes.RESPONSE_FAILED,
158                                 OPERATION_FAILED);
159                         }
160                         break;
161                     default:
162                         LOG.error("unsupported service-type");
163                         return ModelMappingUtils.createServiceImplResponse(ResponseCodes.RESPONSE_FAILED,
164                             OPERATION_FAILED);
165                 }
166                 return ModelMappingUtils.createServiceImplResponse(ResponseCodes.RESPONSE_OK,
167                     OPERATION_SUCCESSFUL);
168             }
169         });
170     }
171
172     @Override
173     public ListenableFuture<ServiceDeleteOutput> serviceDelete(ServiceDeleteInput input, Services service) {
174         String serviceName = input.getServiceName();
175         LOG.info("Calling service delete request {}", serviceName);
176         return executor.submit(new Callable<ServiceDeleteOutput>() {
177
178             @Override
179             public ServiceDeleteOutput call() throws Exception {
180                 sendNotifications(ServicePathNotificationTypes.ServiceDelete, serviceName,
181                         RpcStatusEx.Pending, "Service compliant, submitting service delete Request ...");
182                 // Obtain path description
183                 Optional<
184                     org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev200128.service
185                     .path.PathDescription> pathDescriptionOpt = getPathDescriptionFromDatastore(serviceName);
186                 if (pathDescriptionOpt.isEmpty()) {
187                     LOG.error("Unable to get path description for service {}!", serviceName);
188                     sendNotifications(ServicePathNotificationTypes.ServiceDelete, serviceName,
189                             RpcStatusEx.Failed, "Unable to get path description for service");
190                     return ModelMappingUtils.createServiceDeleteResponse(ResponseCodes.RESPONSE_FAILED,
191                             OPERATION_FAILED);
192                 }
193                 PathDescription pathDescription = pathDescriptionOpt.get();
194                 String serviceType = ServiceTypes.getServiceType(service.getServiceAEnd().getServiceFormat().getName(),
195                     service.getServiceAEnd().getServiceRate(), portMapping.getMapping(service.getServiceAEnd()
196                         .getNodeId().getValue(), service.getServiceAEnd().getTxDirection().getPort().getPortName()));
197                 switch (serviceType) {
198                     case StringConstants.SERVICE_TYPE_100GE_T:
199                     case StringConstants.SERVICE_TYPE_400GE:
200                     case StringConstants.SERVICE_TYPE_OTU4:
201                     case StringConstants.SERVICE_TYPE_OTUC4:
202                         if (!manageServicePathDeletion(serviceName, pathDescription, serviceType)) {
203                             return ModelMappingUtils.createServiceDeleteResponse(ResponseCodes.RESPONSE_FAILED,
204                                 OPERATION_FAILED);
205                         }
206                         break;
207                     case StringConstants.SERVICE_TYPE_1GE:
208                     case StringConstants.SERVICE_TYPE_10GE:
209                     case StringConstants.SERVICE_TYPE_100GE_M:
210                     case StringConstants.SERVICE_TYPE_ODU4:
211                     case StringConstants.SERVICE_TYPE_ODUC4:
212                         if (!manageOtnServicePathDeletion(serviceName, pathDescription, service, serviceType)) {
213                             return ModelMappingUtils.createServiceDeleteResponse(ResponseCodes.RESPONSE_FAILED,
214                                 OPERATION_FAILED);
215                         }
216                         break;
217                     default:
218                         LOG.error("unsupported service-type");
219                         return ModelMappingUtils.createServiceDeleteResponse(ResponseCodes.RESPONSE_FAILED,
220                             OPERATION_FAILED);
221                 }
222                 return ModelMappingUtils.createServiceDeleteResponse(ResponseCodes.RESPONSE_OK, OPERATION_SUCCESSFUL);
223             }
224         });
225     }
226
227     @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(
228         value = "UPM_UNCALLED_PRIVATE_METHOD",
229         justification = "call in call() method")
230     private Uint32 getServiceRate(ServiceImplementationRequestInput input) {
231         if (input.getServiceAEnd() == null) {
232             LOG.warn("Unable to get service-rate for service {}", input.getServiceName());
233             return Uint32.ZERO;
234         }
235         if (input.getServiceAEnd().getServiceRate() != null) {
236             return input.getServiceAEnd().getServiceRate();
237         }
238         Map<ServiceFormat, Map<String, Uint32>> formatRateMap  = Map.of(
239                 ServiceFormat.OTU, Map.of(
240                     "OTUCn", Uint32.valueOf(400),
241                     "OTU4", Uint32.valueOf(100),
242                     "OTU2", Uint32.valueOf(10),
243                     "OTU2e", Uint32.valueOf(10)),
244                 ServiceFormat.ODU, Map.of(
245                     "ODUCn",Uint32.valueOf(400),
246                     "ODU4", Uint32.valueOf(100),
247                     "ODU2", Uint32.valueOf(10),
248                     "ODU2e", Uint32.valueOf(10),
249                     "ODU0", Uint32.valueOf(1)));
250         if (!formatRateMap.containsKey(input.getServiceAEnd().getServiceFormat())) {
251             LOG.warn("Unable to get service-rate for service {} - unsupported service format {}",
252                 input.getServiceName(), input.getServiceAEnd().getServiceFormat());
253             return Uint32.ZERO;
254         }
255         String serviceName =
256             ServiceFormat.OTU.equals(input.getServiceAEnd().getServiceFormat())
257                 ? input.getServiceAEnd().getOtuServiceRate().getSimpleName()
258                 : input.getServiceAEnd().getOduServiceRate().getSimpleName();
259         if (!formatRateMap.get(input.getServiceAEnd().getServiceFormat()).containsKey(serviceName)) {
260             LOG.warn("Unable to get service-rate for service {} - unsupported service name {}",
261                 input.getServiceName(), serviceName);
262             return Uint32.ZERO;
263         }
264         return formatRateMap.get(input.getServiceAEnd().getServiceFormat()).get(serviceName);
265     }
266
267     @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(
268             value = "UPM_UNCALLED_PRIVATE_METHOD",
269             justification = "call in call() method")
270     private ServicePowerTurndownOutput olmPowerTurndown(ServicePathInputData servicePathInputData)
271             throws InterruptedException, ExecutionException, TimeoutException {
272         LOG.debug(TURNING_DOWN_POWER_ON_A_TO_Z_PATH_MSG);
273         Future<RpcResult<ServicePowerTurndownOutput>> powerTurndownFuture = this.olmService.servicePowerTurndown(
274                 new ServicePowerTurndownInputBuilder(servicePathInputData.getServicePathInput()).build());
275         return powerTurndownFuture.get(Timeouts.DATASTORE_READ, TimeUnit.MILLISECONDS).getResult();
276     }
277
278     @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(
279             value = "UPM_UNCALLED_PRIVATE_METHOD",
280             justification = "call in call() method")
281     private Optional<org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev200128
282         .service.path.PathDescription> getPathDescriptionFromDatastore(String serviceName) {
283         InstanceIdentifier<org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev200128
284             .service.path.PathDescription> pathDescriptionIID = InstanceIdentifier.create(ServicePathList.class)
285                 .child(ServicePaths.class, new ServicePathsKey(serviceName))
286                 .child(org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev200128
287                     .service.path.PathDescription.class);
288         ReadTransaction pathDescReadTx = this.dataBroker.newReadOnlyTransaction();
289         try {
290             LOG.debug("Getting path description for service {}", serviceName);
291             return pathDescReadTx.read(LogicalDatastoreType.OPERATIONAL, pathDescriptionIID)
292                     .get(Timeouts.DATASTORE_READ, TimeUnit.MILLISECONDS);
293         } catch (InterruptedException | ExecutionException | TimeoutException e) {
294             LOG.warn("Exception while getting path description from datastore {} for service {}!", pathDescriptionIID,
295                     serviceName, e);
296             return Optional.empty();
297         }
298     }
299
300     @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(
301             value = "UPM_UNCALLED_PRIVATE_METHOD",
302             justification = "call in call() method")
303     private List<DeviceRenderingResult> deviceRendering(RollbackProcessor rollbackProcessor,
304             ServicePathInputData servicePathDataAtoZ, ServicePathInputData servicePathDataZtoA) {
305         LOG.info(RENDERING_DEVICES_A_Z_MSG);
306         sendNotifications(ServicePathNotificationTypes.ServiceImplementationRequest,
307                 servicePathDataAtoZ.getServicePathInput().getServiceName(), RpcStatusEx.Pending,
308                 RENDERING_DEVICES_A_Z_MSG);
309         ListenableFuture<DeviceRenderingResult> atozrenderingFuture =
310                 this.executor.submit(new DeviceRenderingTask(this.deviceRenderer, servicePathDataAtoZ,
311                         ServicePathDirection.A_TO_Z));
312
313         LOG.info("Rendering devices Z-A");
314         sendNotifications(ServicePathNotificationTypes.ServiceImplementationRequest,
315                 servicePathDataZtoA.getServicePathInput().getServiceName(), RpcStatusEx.Pending,
316                 RENDERING_DEVICES_Z_A_MSG);
317         ListenableFuture<DeviceRenderingResult> ztoarenderingFuture =
318                 this.executor.submit(new DeviceRenderingTask(this.deviceRenderer, servicePathDataZtoA,
319                         ServicePathDirection.Z_TO_A));
320         ListenableFuture<List<DeviceRenderingResult>> renderingCombinedFuture =
321                 Futures.allAsList(atozrenderingFuture, ztoarenderingFuture);
322
323         List<DeviceRenderingResult> renderingResults = new ArrayList<>(2);
324         try {
325             LOG.info("Waiting for A-Z and Z-A device renderers ...");
326             renderingResults = renderingCombinedFuture.get(Timeouts.RENDERING_TIMEOUT, TimeUnit.MILLISECONDS);
327         } catch (InterruptedException | ExecutionException | TimeoutException e) {
328             LOG.warn(DEVICE_RENDERING_ROLL_BACK_MSG, e);
329             sendNotifications(ServicePathNotificationTypes.ServiceImplementationRequest,
330                     servicePathDataAtoZ.getServicePathInput().getServiceName(), RpcStatusEx.Pending,
331                     DEVICE_RENDERING_ROLL_BACK_MSG);
332             //FIXME we can't do rollback here, because we don't have rendering results.
333             return renderingResults;
334         }
335
336         rollbackProcessor.addTask(new DeviceRenderingRollbackTask("AtoZDeviceTask",
337                 ! renderingResults.get(0).isSuccess(), renderingResults.get(0).getRenderedNodeInterfaces(),
338                 this.deviceRenderer));
339         rollbackProcessor.addTask(new DeviceRenderingRollbackTask("ZtoADeviceTask",
340                 ! renderingResults.get(1).isSuccess(), renderingResults.get(1).getRenderedNodeInterfaces(),
341                 this.deviceRenderer));
342         return renderingResults;
343     }
344
345     @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(
346         value = "UPM_UNCALLED_PRIVATE_METHOD",
347         justification = "call in call() method")
348     private List<OtnDeviceRenderingResult> otnDeviceRendering(RollbackProcessor rollbackProcessor,
349         OtnServicePathInput otnServicePathAtoZ, OtnServicePathInput otnServicePathZtoA) {
350         LOG.info(RENDERING_DEVICES_A_Z_MSG);
351         sendNotifications(ServicePathNotificationTypes.ServiceImplementationRequest,
352             otnServicePathAtoZ.getServiceName(), RpcStatusEx.Pending,
353             RENDERING_DEVICES_A_Z_MSG);
354         ListenableFuture<OtnDeviceRenderingResult> atozrenderingFuture =
355             this.executor.submit(new OtnDeviceRenderingTask(this.otnDeviceRenderer, otnServicePathAtoZ));
356         LOG.info(RENDERING_DEVICES_Z_A_MSG);
357         sendNotifications(ServicePathNotificationTypes.ServiceImplementationRequest,
358             otnServicePathZtoA.getServiceName(), RpcStatusEx.Pending,
359             RENDERING_DEVICES_Z_A_MSG);
360         ListenableFuture<OtnDeviceRenderingResult> ztoarenderingFuture =
361             this.executor.submit(new OtnDeviceRenderingTask(this.otnDeviceRenderer, otnServicePathZtoA));
362         ListenableFuture<List<OtnDeviceRenderingResult>> renderingCombinedFuture =
363             Futures.allAsList(atozrenderingFuture, ztoarenderingFuture);
364         List<OtnDeviceRenderingResult> otnRenderingResults = new ArrayList<>(2);
365         try {
366             LOG.info("Waiting for A-Z and Z-A device renderers ...");
367             otnRenderingResults = renderingCombinedFuture.get(Timeouts.RENDERING_TIMEOUT, TimeUnit.MILLISECONDS);
368         } catch (InterruptedException | ExecutionException | TimeoutException e) {
369             LOG.warn(DEVICE_RENDERING_ROLL_BACK_MSG, e);
370             sendNotifications(ServicePathNotificationTypes.ServiceImplementationRequest,
371                 otnServicePathAtoZ.getServiceName(), RpcStatusEx.Pending,
372                 DEVICE_RENDERING_ROLL_BACK_MSG);
373             //FIXME we can't do rollback here, because we don't have rendering results.
374             return otnRenderingResults;
375         }
376         for (int i = 0; i < otnRenderingResults.size(); i++) {
377             rollbackProcessor.addTask(new DeviceRenderingRollbackTask("DeviceTask n° " + i + 1,
378                 ! otnRenderingResults.get(i).isSuccess(), otnRenderingResults.get(i).getRenderedNodeInterfaces(),
379                 this.deviceRenderer));
380         }
381         return otnRenderingResults;
382     }
383
384     @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(
385             value = "UPM_UNCALLED_PRIVATE_METHOD",
386             justification = "call in call() method")
387     private void olmPowerSetup(RollbackProcessor rollbackProcessor, ServicePowerSetupInput powerSetupInputAtoZ,
388             ServicePowerSetupInput powerSetupInputZtoA) {
389         LOG.info("Olm power setup A-Z");
390         sendNotifications(ServicePathNotificationTypes.ServiceImplementationRequest,
391                 powerSetupInputAtoZ.getServiceName(), RpcStatusEx.Pending, "Olm power setup A-Z");
392         ListenableFuture<OLMRenderingResult> olmPowerSetupFutureAtoZ
393                 = this.executor.submit(new OlmPowerSetupTask(this.olmService, powerSetupInputAtoZ));
394
395         LOG.info("OLM power setup Z-A");
396         sendNotifications(ServicePathNotificationTypes.ServiceImplementationRequest,
397                 powerSetupInputAtoZ.getServiceName(), RpcStatusEx.Pending, "Olm power setup Z-A");
398         ListenableFuture<OLMRenderingResult> olmPowerSetupFutureZtoA
399                 = this.executor.submit(new OlmPowerSetupTask(this.olmService, powerSetupInputZtoA));
400         ListenableFuture<List<OLMRenderingResult>> olmFutures =
401                 Futures.allAsList(olmPowerSetupFutureAtoZ, olmPowerSetupFutureZtoA);
402
403         List<OLMRenderingResult> olmResults;
404         try {
405             LOG.info("Waiting for A-Z and Z-A OLM power setup ...");
406             olmResults = olmFutures.get(Timeouts.OLM_TIMEOUT, TimeUnit.MILLISECONDS);
407         } catch (InterruptedException | ExecutionException | TimeoutException e) {
408             LOG.warn(OLM_ROLL_BACK_MSG, e);
409             sendNotifications(ServicePathNotificationTypes.ServiceImplementationRequest,
410                     powerSetupInputAtoZ.getServiceName(), RpcStatusEx.Pending,
411                     OLM_ROLL_BACK_MSG);
412             rollbackProcessor.addTask(new OlmPowerSetupRollbackTask("AtoZOLMTask", true,
413                     this.olmService, powerSetupInputAtoZ));
414             rollbackProcessor.addTask(new OlmPowerSetupRollbackTask("ZtoAOLMTask", true,
415                     this.olmService, powerSetupInputZtoA));
416             return;
417         }
418
419         rollbackProcessor.addTask(new OlmPowerSetupRollbackTask("AtoZOLMTask", ! olmResults.get(0).isSuccess(),
420                 this.olmService, powerSetupInputAtoZ));
421         rollbackProcessor.addTask(new OlmPowerSetupRollbackTask("ZtoAOLMTask", ! olmResults.get(1).isSuccess(),
422                 this.olmService, powerSetupInputZtoA));
423     }
424
425     @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(
426             value = "UPM_UNCALLED_PRIVATE_METHOD",
427             justification = "call in call() method")
428     private boolean isServiceActivated(String nodeId, String tpId) {
429         LOG.info("Starting service activation test on node {} and tp {}", nodeId, tpId);
430         for (int i = 0; i < 3; i++) {
431             List<Measurements> measurements = getMeasurements(nodeId, tpId);
432             if (measurements == null) {
433                 LOG.warn("Device {} is not reporting PreFEC on TP: {}", nodeId, tpId);
434                 return true;
435             }
436             if (verifyPreFecBer(measurements)) {
437                 return true;
438             }
439             try {
440                 Thread.sleep(Timeouts.SERVICE_ACTIVATION_TEST_RETRY_TIME);
441             } catch (InterruptedException ex) {
442                 Thread.currentThread().interrupt();
443             }
444         }
445         LOG.error("Service activation test failed on node {} and termination point {}!", nodeId, tpId);
446         return false;
447     }
448
449     private List<Measurements> getMeasurements(String nodeId, String tp) {
450         GetPmInputBuilder getPmIpBldr = new GetPmInputBuilder()
451             .setNodeId(nodeId)
452             .setGranularity(PmGranularity._15min)
453             .setResourceIdentifier(new ResourceIdentifierBuilder().setResourceName(tp + "-OTU").build())
454             .setResourceType(ResourceTypeEnum.Interface);
455
456         try {
457             Future<RpcResult<GetPmOutput>> getPmFuture = this.olmService.getPm(getPmIpBldr.build());
458             RpcResult<GetPmOutput> getPmRpcResult = getPmFuture.get();
459             GetPmOutput getPmOutput = getPmRpcResult.getResult();
460             if ((getPmOutput != null) && (getPmOutput.getNodeId() != null)) {
461                 LOG.info("successfully finished calling OLM's get PM");
462                 return getPmOutput.getMeasurements();
463                 // may return null
464             } else {
465                 LOG.warn("OLM's get PM failed for node {} and tp {}", nodeId, tp);
466             }
467
468         } catch (ExecutionException | InterruptedException e) {
469             LOG.warn("Error occurred while getting PM for node {} and tp {}", nodeId, tp, e);
470         }
471         return null;
472     }
473
474     private boolean verifyPreFecBer(List<Measurements> measurements) {
475         double preFecCorrectedErrors = Double.MIN_VALUE;
476         double fecUncorrectableBlocks = Double.MIN_VALUE;
477
478         for (Measurements measurement : measurements) {
479             switch (measurement.getPmparameterName()) {
480                 case "preFECCorrectedErrors":
481                     preFecCorrectedErrors = Double.parseDouble(measurement.getPmparameterValue());
482                     break;
483                 case "FECUncorrectableBlocks":
484                     fecUncorrectableBlocks = Double.parseDouble(measurement.getPmparameterValue());
485                     break;
486                 default:
487                     break;
488             }
489         }
490
491         LOG.info("Measurements: preFECCorrectedErrors = {}; FECUncorrectableBlocks = {}", preFecCorrectedErrors,
492                 fecUncorrectableBlocks);
493
494         if (fecUncorrectableBlocks > Double.MIN_VALUE) {
495             LOG.error("Data has uncorrectable errors, BER test failed");
496             return false;
497         }
498
499         double numOfBitsPerSecond = 112000000000d;
500         double threshold = 0.00002d;
501         double result = preFecCorrectedErrors / numOfBitsPerSecond;
502         LOG.info("PreFEC value is {}", Double.toString(result));
503         return result <= threshold;
504     }
505
506     @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(
507         value = "UPM_UNCALLED_PRIVATE_METHOD",
508         justification = "call in call() method")
509     private boolean manageServicePathCreation(ServiceImplementationRequestInput input, String serviceType) {
510         ServicePathInputData servicePathInputDataAtoZ = ModelMappingUtils
511             .rendererCreateServiceInputAToZ(input.getServiceName(), input.getPathDescription(), Action.Create);
512         ServicePathInputData servicePathInputDataZtoA = ModelMappingUtils
513             .rendererCreateServiceInputZToA(input.getServiceName(), input.getPathDescription(), Action.Create);
514         // Rollback should be same for all conditions, so creating a new one
515         RollbackProcessor rollbackProcessor = new RollbackProcessor();
516         List<DeviceRenderingResult> renderingResults =
517             deviceRendering(rollbackProcessor, servicePathInputDataAtoZ, servicePathInputDataZtoA);
518         if (rollbackProcessor.rollbackAllIfNecessary() > 0) {
519             sendNotifications(ServicePathNotificationTypes.ServiceImplementationRequest,
520                 input.getServiceName(), RpcStatusEx.Failed, DEVICE_RENDERING_ROLL_BACK_MSG);
521             return false;
522         }
523         ServicePowerSetupInput olmPowerSetupInputAtoZ =
524             ModelMappingUtils.createServicePowerSetupInput(renderingResults.get(0).getOlmList(), input);
525         ServicePowerSetupInput olmPowerSetupInputZtoA =
526             ModelMappingUtils.createServicePowerSetupInput(renderingResults.get(1).getOlmList(), input);
527         olmPowerSetup(rollbackProcessor, olmPowerSetupInputAtoZ, olmPowerSetupInputZtoA);
528         if (rollbackProcessor.rollbackAllIfNecessary() > 0) {
529             sendNotifications(ServicePathNotificationTypes.ServiceImplementationRequest,
530                 input.getServiceName(), RpcStatusEx.Failed, OLM_ROLL_BACK_MSG);
531             return false;
532         }
533         // run service activation test twice - once on source node and once on
534         // destination node
535         List<Nodes> nodes = servicePathInputDataAtoZ.getServicePathInput().getNodes();
536         if ((nodes == null) || (nodes.isEmpty())) {
537             return false;
538         }
539
540         Nodes sourceNode = nodes.get(0);
541         Nodes destNode = nodes.get(nodes.size() - 1);
542         String srcNetworkTp =
543             sourceNode.getDestTp().contains(StringConstants.NETWORK_TOKEN)
544                 ? sourceNode.getDestTp()
545                 : sourceNode.getSrcTp();
546         String dstNetowrkTp =
547             destNode.getDestTp().contains(StringConstants.NETWORK_TOKEN)
548                 ? destNode.getDestTp()
549                 : destNode.getSrcTp();
550
551         if (!isServiceActivated(sourceNode.getNodeId(), srcNetworkTp)
552             || !isServiceActivated(destNode.getNodeId(), dstNetowrkTp)) {
553             rollbackProcessor.rollbackAll();
554             sendNotifications(ServicePathNotificationTypes.ServiceImplementationRequest,
555                 input.getServiceName(), RpcStatusEx.Failed,
556                 "Service activation test failed.");
557             return false;
558         }
559         List<LinkTp> otnLinkTerminationPoints = new ArrayList<>();
560         renderingResults.forEach(rr -> otnLinkTerminationPoints.addAll(rr.getOtnLinkTps()));
561         Link notifLink = createLinkForNotif(otnLinkTerminationPoints);
562
563         sendNotificationsWithPathDescription(ServicePathNotificationTypes.ServiceImplementationRequest,
564             input.getServiceName(), RpcStatusEx.Successful, OPERATION_SUCCESSFUL, input.getPathDescription(),
565             notifLink, serviceType);
566         return true;
567     }
568
569     @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(
570         value = "UPM_UNCALLED_PRIVATE_METHOD",
571         justification = "call in call() method")
572     private boolean manageServicePathDeletion(String serviceName, PathDescription pathDescription, String serviceType)
573             throws InterruptedException {
574         ServicePathInputData servicePathInputDataAtoZ =
575             ModelMappingUtils.rendererCreateServiceInputAToZ(serviceName, pathDescription, Action.Delete);
576         ServicePathInputData servicePathInputDataZtoA =
577             ModelMappingUtils.rendererCreateServiceInputZToA(serviceName, pathDescription, Action.Delete);
578         // OLM turn down power
579         try {
580             LOG.debug(TURNING_DOWN_POWER_ON_A_TO_Z_PATH_MSG);
581             sendNotifications(ServicePathNotificationTypes.ServiceDelete, serviceName,
582                 RpcStatusEx.Pending, TURNING_DOWN_POWER_ON_A_TO_Z_PATH_MSG);
583             ServicePowerTurndownOutput atozPowerTurndownOutput = olmPowerTurndown(servicePathInputDataAtoZ);
584             // TODO add some flag rather than string
585             if (FAILED.equals(atozPowerTurndownOutput.getResult())) {
586                 LOG.error("Service power turndown failed on A-to-Z path for service {}!", serviceName);
587                 sendNotifications(ServicePathNotificationTypes.ServiceDelete, serviceName, RpcStatusEx.Failed,
588                         "Service power turndown failed on A-to-Z path for service");
589                 return false;
590             }
591             LOG.debug("Turning down power on Z-to-A path");
592             sendNotifications(ServicePathNotificationTypes.ServiceDelete, serviceName, RpcStatusEx.Pending,
593                     "Turning down power on Z-to-A path");
594             ServicePowerTurndownOutput ztoaPowerTurndownOutput = olmPowerTurndown(servicePathInputDataZtoA);
595             // TODO add some flag rather than string
596             if (FAILED.equals(ztoaPowerTurndownOutput.getResult())) {
597                 LOG.error("Service power turndown failed on Z-to-A path for service {}!", serviceName);
598                 sendNotifications(ServicePathNotificationTypes.ServiceDelete, serviceName, RpcStatusEx.Failed,
599                         "Service power turndown failed on Z-to-A path for service");
600                 return false;
601             }
602         } catch (InterruptedException | ExecutionException | TimeoutException e) {
603             LOG.error("Error while turning down power!", e);
604             return false;
605         }
606         // delete service path with renderer
607         LOG.info("Deleting service path via renderer");
608         sendNotifications(ServicePathNotificationTypes.ServiceDelete, serviceName, RpcStatusEx.Pending,
609                 "Deleting service path via renderer");
610         RollbackProcessor rollbackProcessor = new RollbackProcessor();
611         List<DeviceRenderingResult> renderingResults =
612             deviceRendering(rollbackProcessor, servicePathInputDataAtoZ, servicePathInputDataZtoA);
613         List<LinkTp> otnLinkTerminationPoints = new ArrayList<>();
614         renderingResults.forEach(rr -> otnLinkTerminationPoints.addAll(rr.getOtnLinkTps()));
615         Link notifLink = createLinkForNotif(otnLinkTerminationPoints);
616
617         sendNotificationsWithPathDescription(ServicePathNotificationTypes.ServiceDelete,
618                 serviceName, RpcStatusEx.Successful, OPERATION_SUCCESSFUL, pathDescription, notifLink, serviceType);
619         return true;
620     }
621
622     @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(
623         value = "UPM_UNCALLED_PRIVATE_METHOD",
624         justification = "call in call() method")
625     private boolean manageOtnServicePathCreation(ServiceImplementationRequestInput input, String serviceType,
626             Uint32 serviceRate) {
627         // This is A-Z side
628         OtnServicePathInput otnServicePathInputAtoZ = ModelMappingUtils
629             .rendererCreateOtnServiceInput(input.getServiceName(), Action.Create,
630                 input.getServiceAEnd().getServiceFormat().getName(),
631                 serviceRate,
632                 input.getPathDescription(), true);
633         // This is Z-A side
634         OtnServicePathInput otnServicePathInputZtoA = ModelMappingUtils
635             .rendererCreateOtnServiceInput(input.getServiceName(), Action.Create,
636                 input.getServiceZEnd().getServiceFormat().getName(),
637                 serviceRate,
638                 input.getPathDescription(), false);
639         // Rollback should be same for all conditions, so creating a new one
640         RollbackProcessor rollbackProcessor = new RollbackProcessor();
641         List<OtnDeviceRenderingResult> renderingResults =
642             otnDeviceRendering(rollbackProcessor, otnServicePathInputAtoZ, otnServicePathInputZtoA);
643         if (rollbackProcessor.rollbackAllIfNecessary() > 0) {
644             rollbackProcessor.rollbackAll();
645             sendNotifications(ServicePathNotificationTypes.ServiceImplementationRequest,
646                 input.getServiceName(), RpcStatusEx.Failed, DEVICE_RENDERING_ROLL_BACK_MSG);
647             return false;
648         }
649         List<LinkTp> otnLinkTerminationPoints = new ArrayList<>();
650         renderingResults.forEach(rr -> otnLinkTerminationPoints.addAll(rr.getOtnLinkTps()));
651         Link notifLink = createLinkForNotif(otnLinkTerminationPoints);
652
653         sendNotificationsWithPathDescription(ServicePathNotificationTypes.ServiceImplementationRequest,
654             input.getServiceName(), RpcStatusEx.Successful, OPERATION_SUCCESSFUL, input.getPathDescription(),
655             notifLink, serviceType);
656         return true;
657     }
658
659     @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(
660         value = "UPM_UNCALLED_PRIVATE_METHOD",
661         justification = "call in call() method")
662     private boolean manageOtnServicePathDeletion(String serviceName, PathDescription pathDescription,
663             Services service, String serviceType) {
664         // This is A-Z side
665         OtnServicePathInput otnServicePathInputAtoZ = ModelMappingUtils
666             .rendererCreateOtnServiceInput(serviceName, Action.Delete,
667                 service.getServiceAEnd().getServiceFormat().getName(),
668                 service.getServiceAEnd().getServiceRate(),
669                 pathDescription, true);
670         // This is Z-A side
671         OtnServicePathInput otnServicePathInputZtoA = ModelMappingUtils
672             .rendererCreateOtnServiceInput(serviceName, Action.Delete,
673                 service.getServiceZEnd().getServiceFormat().getName(),
674                 service.getServiceAEnd().getServiceRate(),
675                 pathDescription, false);
676         LOG.info("Deleting otn-service path {} via renderer", serviceName);
677         sendNotifications(ServicePathNotificationTypes.ServiceDelete, serviceName, RpcStatusEx.Pending,
678                 "Deleting otn-service path via renderer");
679
680         RollbackProcessor rollbackProcessor = new RollbackProcessor();
681         List<OtnDeviceRenderingResult> renderingResults =
682             otnDeviceRendering(rollbackProcessor, otnServicePathInputAtoZ, otnServicePathInputZtoA);
683
684         List<LinkTp> otnLinkTerminationPoints = new ArrayList<>();
685         renderingResults.forEach(rr -> otnLinkTerminationPoints.addAll(rr.getOtnLinkTps()));
686         Link notifLink = createLinkForNotif(otnLinkTerminationPoints);
687
688         sendNotificationsWithPathDescription(ServicePathNotificationTypes.ServiceDelete,
689                 serviceName, RpcStatusEx.Successful, OPERATION_SUCCESSFUL, pathDescription, notifLink, serviceType);
690         return true;
691     }
692
693     /**
694      * Send renderer notification.
695      * @param servicePathNotificationTypes ServicePathNotificationTypes
696      * @param serviceName String
697      * @param rpcStatusEx RpcStatusEx
698      * @param message String
699      */
700     private void sendNotifications(ServicePathNotificationTypes servicePathNotificationTypes, String serviceName,
701             RpcStatusEx rpcStatusEx, String message) {
702         Notification notification = buildNotification(servicePathNotificationTypes, serviceName, rpcStatusEx, message,
703                 null, null, null);
704         send(notification);
705     }
706
707     /**
708      * Send renderer notification with path description information.
709      * @param servicePathNotificationTypes ServicePathNotificationTypes
710      * @param serviceName String
711      * @param rpcStatusEx RpcStatusEx
712      * @param message String
713      * @param pathDescription PathDescription
714      */
715     private void sendNotificationsWithPathDescription(ServicePathNotificationTypes servicePathNotificationTypes,
716             String serviceName, RpcStatusEx rpcStatusEx, String message, PathDescription pathDescription,
717             Link notifLink, String serviceType) {
718         Notification notification = buildNotification(servicePathNotificationTypes, serviceName, rpcStatusEx, message,
719                 pathDescription, notifLink, serviceType);
720         send(notification);
721     }
722
723     /**
724      * Build notification containing path description information.
725      * @param servicePathNotificationTypes ServicePathNotificationTypes
726      * @param serviceName String
727      * @param rpcStatusEx RpcStatusEx
728      * @param message String
729      * @param pathDescription PathDescription
730      * @return notification with RendererRpcResultSp type.
731      */
732     private RendererRpcResultSp buildNotification(ServicePathNotificationTypes servicePathNotificationTypes,
733             String serviceName, RpcStatusEx rpcStatusEx, String message, PathDescription pathDescription,
734             Link notifLink, String serviceType) {
735         RendererRpcResultSpBuilder builder = new RendererRpcResultSpBuilder()
736                 .setNotificationType(servicePathNotificationTypes).setServiceName(serviceName).setStatus(rpcStatusEx)
737                 .setStatusMessage(message)
738                 .setServiceType(serviceType);
739         if (pathDescription != null) {
740             builder.setAToZDirection(pathDescription.getAToZDirection())
741                 .setZToADirection(pathDescription.getZToADirection());
742         }
743         if (notifLink != null) {
744             builder.setLink(notifLink);
745         }
746         return builder.build();
747     }
748
749     /**
750      * Send renderer notification.
751      * @param notification Notification
752      */
753     private void send(Notification notification) {
754         try {
755             LOG.info("Sending notification {}", notification);
756             notificationPublishService.putNotification(notification);
757         } catch (InterruptedException e) {
758             LOG.info("notification offer rejected: ", e);
759             Thread.currentThread().interrupt();
760         }
761     }
762
763     private Link createLinkForNotif(List<LinkTp> otnLinkTerminationPoints) {
764         if (otnLinkTerminationPoints == null || otnLinkTerminationPoints.size() != 2) {
765             return null;
766         }
767         return new LinkBuilder()
768                 .setATermination(new ATerminationBuilder()
769                     .setNodeId(otnLinkTerminationPoints.get(0).getNodeId())
770                     .setTpId(otnLinkTerminationPoints.get(0).getTpId())
771                     .build())
772                 .setZTermination(new ZTerminationBuilder()
773                     .setNodeId(otnLinkTerminationPoints.get(1).getNodeId())
774                     .setTpId(otnLinkTerminationPoints.get(1).getTpId())
775                     .build())
776                 .build();
777     }
778 }