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