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