Service-notification handling for Renderer
[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.base.Optional;
11 import com.google.common.util.concurrent.Futures;
12 import com.google.common.util.concurrent.ListenableFuture;
13 import com.google.common.util.concurrent.ListeningExecutorService;
14 import com.google.common.util.concurrent.MoreExecutors;
15
16 import java.util.ArrayList;
17 import java.util.List;
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
25 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
26 import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
27 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
28 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
29 import org.opendaylight.transportpce.common.ResponseCodes;
30 import org.opendaylight.transportpce.common.Timeouts;
31 import org.opendaylight.transportpce.common.openroadminterfaces.OpenRoadmInterfacesImpl;
32 import org.opendaylight.transportpce.renderer.ModelMappingUtils;
33 import org.opendaylight.transportpce.renderer.NetworkModelWavelengthService;
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.RollbackProcessor;
41 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.olm.rev170418.GetPmInputBuilder;
42 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.olm.rev170418.GetPmOutput;
43 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.olm.rev170418.ServicePowerSetupInput;
44 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.olm.rev170418.ServicePowerTurndownInputBuilder;
45 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.olm.rev170418.ServicePowerTurndownOutput;
46 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.olm.rev170418.TransportpceOlmService;
47 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.olm.rev170418.get.pm.output.Measurements;
48 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev171017.ServiceDeleteInput;
49 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev171017.ServiceDeleteOutput;
50 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev171017.ServiceImplementationRequestInput;
51 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev171017.ServiceImplementationRequestOutput;
52 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev171017.ServiceRpcResultSp;
53 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev171017.ServiceRpcResultSpBuilder;
54 import org.opendaylight.yang.gen.v1.http.org.openroadm.pm.types.rev161014.PmGranularity;
55 import org.opendaylight.yang.gen.v1.http.org.openroadm.resource.types.rev161014.ResourceTypeEnum;
56 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev171016.RpcStatusEx;
57 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev171016.ServicePathNotificationTypes;
58 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev171016.service.path.PathDescription;
59 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.servicepath.rev171017.ServicePathList;
60 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.servicepath.rev171017.service.path.list.ServicePaths;
61 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.servicepath.rev171017.service.path.list.ServicePathsKey;
62 import org.opendaylight.yang.gen.v1.http.org.transportpce.common.types.rev170907.olm.get.pm.input.ResourceIdentifierBuilder;
63 import org.opendaylight.yang.gen.v1.http.org.transportpce.common.types.rev170907.olm.renderer.input.Nodes;
64 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
65 import org.opendaylight.yangtools.yang.common.RpcResult;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
68
69
70 public class RendererServiceOperationsImpl implements RendererServiceOperations {
71
72     private static final Logger LOG = LoggerFactory.getLogger(RendererServiceOperationsImpl.class);
73     private static final String FAILED = "Failed";
74     private static final String OPERATION_FAILED = "Operation Failed";
75     private static final String OPERATION_SUCCESSFUL = "Operation Successful";
76     private static final int NUMBER_OF_THREADS = 4;
77
78     private final DeviceRendererService deviceRenderer;
79     private final TransportpceOlmService olmService;
80     private final DataBroker dataBroker;
81     private final NotificationPublishService notificationPublishService;
82     private ListeningExecutorService executor;
83     private NetworkModelWavelengthService networkModelWavelengthService;
84     private ServiceRpcResultSp notification = null;
85
86     public RendererServiceOperationsImpl(DeviceRendererService deviceRenderer, TransportpceOlmService olmService,
87             DataBroker dataBroker, NetworkModelWavelengthService networkModelWavelengthService,
88             NotificationPublishService notificationPublishService) {
89         this.deviceRenderer = deviceRenderer;
90         this.olmService = olmService;
91         this.dataBroker = dataBroker;
92         this.networkModelWavelengthService = networkModelWavelengthService;
93         this.notificationPublishService = notificationPublishService;
94         this.executor = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(NUMBER_OF_THREADS));
95     }
96
97     private void sendNotifications(ServicePathNotificationTypes servicePathNotificationTypes, String serviceName,
98             RpcStatusEx rpcStatusEx, String message) {
99         this.notification = new ServiceRpcResultSpBuilder()
100                 .setNotificationType(servicePathNotificationTypes)
101                 .setServiceName(serviceName)
102                 .setStatus(rpcStatusEx)
103                 .setStatusMessage(message)
104                 .build();
105         try {
106             notificationPublishService.putNotification(this.notification);
107         } catch (InterruptedException e) {
108             LOG.info("notification offer rejected : ", e.getMessage());
109         }
110     }
111
112     @Override
113     public ListenableFuture<ServiceImplementationRequestOutput>
114             serviceImplementation(ServiceImplementationRequestInput input) {
115         LOG.info("Calling service impl request {} {}", input.getServiceName());
116         return executor.submit(new Callable<ServiceImplementationRequestOutput>() {
117
118             @Override
119             public ServiceImplementationRequestOutput call() throws Exception {
120                 sendNotifications(ServicePathNotificationTypes.ServiceImplementationRequest, input.getServiceName(),
121                         RpcStatusEx.Pending, "Service compliant, submitting service implementation Request ...");
122                 RollbackProcessor rollbackProcessor = new RollbackProcessor();
123                 ServicePathInputData servicePathInputDataAtoZ = ModelMappingUtils
124                         .rendererCreateServiceInputAToZ(input.getServiceName(), input.getPathDescription());
125                 ServicePathInputData servicePathInputDataZtoA = ModelMappingUtils
126                         .rendererCreateServiceInputZToA(input.getServiceName(), input.getPathDescription());
127                 List<DeviceRenderingResult> renderingResults =
128                         deviceRendering(rollbackProcessor, servicePathInputDataAtoZ, servicePathInputDataZtoA);
129                 if (rollbackProcessor.rollbackAllIfNecessary() > 0) {
130                     sendNotifications(ServicePathNotificationTypes.ServiceImplementationRequest, input.getServiceName(),
131                             RpcStatusEx.Failed, "Device rendering was not successful! Rendering will be rolled back.");
132                     return ModelMappingUtils.createServiceImplResponse(ResponseCodes.RESPONSE_FAILED, OPERATION_FAILED);
133                 }
134                 ServicePowerSetupInput olmPowerSetupInputAtoZ =
135                         ModelMappingUtils.createServicePowerSetupInput(renderingResults.get(0).getOlmList(), input);
136                 ServicePowerSetupInput olmPowerSetupInputZtoA =
137                         ModelMappingUtils.createServicePowerSetupInput(renderingResults.get(1).getOlmList(), input);
138                 olmPowerSetup(rollbackProcessor, olmPowerSetupInputAtoZ, olmPowerSetupInputZtoA);
139                 if (rollbackProcessor.rollbackAllIfNecessary() > 0) {
140                     sendNotifications(ServicePathNotificationTypes.ServiceImplementationRequest, input.getServiceName(),
141                             RpcStatusEx.Failed,
142                             "OLM power setup was not successful! Rendering and OLM will be rolled back.");
143                     return ModelMappingUtils.createServiceImplResponse(ResponseCodes.RESPONSE_FAILED, OPERATION_FAILED);
144                 }
145                 // run service activation test twice - once on source node and once on
146                 // destination node
147                 List<Nodes> nodes = servicePathInputDataAtoZ.getServicePathInput().getNodes();
148                 Nodes sourceNode = nodes.get(0);
149                 Nodes destNode = nodes.get(nodes.size() - 1);
150                 String srcNetworkTp;
151                 String dstNetowrkTp;
152                 if (sourceNode.getDestTp().contains(OpenRoadmInterfacesImpl.NETWORK_TOKEN)) {
153                     srcNetworkTp = sourceNode.getDestTp();
154                 } else {
155                     srcNetworkTp = sourceNode.getSrcTp();
156                 }
157                 if (destNode.getDestTp().contains(OpenRoadmInterfacesImpl.NETWORK_TOKEN)) {
158                     dstNetowrkTp = destNode.getDestTp();
159                 } else {
160                     dstNetowrkTp = destNode.getSrcTp();
161                 }
162                 if (!isServiceActivated(sourceNode.getNodeId(), srcNetworkTp)
163                         || !isServiceActivated(destNode.getNodeId(), dstNetowrkTp)) {
164                     rollbackProcessor.rollbackAll();
165                     sendNotifications(ServicePathNotificationTypes.ServiceImplementationRequest, input.getServiceName(),
166                             RpcStatusEx.Failed, "Service activation test failed.");
167                     return ModelMappingUtils.createServiceImplResponse(ResponseCodes.RESPONSE_FAILED, OPERATION_FAILED);
168                 }
169                 // If Service activation is success update Network ModelMappingUtils
170                 networkModelWavelengthService.useWavelengths(input.getPathDescription());
171                 sendNotifications(ServicePathNotificationTypes.ServiceImplementationRequest, input.getServiceName(),
172                         RpcStatusEx.Successful, OPERATION_SUCCESSFUL);
173                 return ModelMappingUtils.createServiceImplResponse(ResponseCodes.RESPONSE_OK, OPERATION_SUCCESSFUL);
174             }
175         });
176
177     }
178
179     @Override
180     public ListenableFuture<ServiceDeleteOutput> serviceDelete(ServiceDeleteInput input) {
181         String serviceName = input.getServiceName();
182         LOG.info("Calling service delete request {} {}", input.getServiceName());
183         return executor.submit(new Callable<ServiceDeleteOutput>() {
184
185             @Override
186             public ServiceDeleteOutput call() throws Exception {
187                 sendNotifications(ServicePathNotificationTypes.ServiceDelete, input.getServiceName(),
188                         RpcStatusEx.Pending, "Service compliant, submitting service delete Request ...");
189                 // Obtain path description
190                 Optional<PathDescription> pathDescriptionOpt = getPathDescriptionFromDatastore(serviceName);
191                 PathDescription pathDescription;
192                 if (pathDescriptionOpt.isPresent()) {
193                     pathDescription = pathDescriptionOpt.get();
194                 } else {
195                     LOG.error("Unable to get path description for service {}!", serviceName);
196                     sendNotifications(ServicePathNotificationTypes.ServiceDelete, input.getServiceName(),
197                             RpcStatusEx.Failed, "Unable to get path description for service");
198                     return ModelMappingUtils.createServiceDeleteResponse(ResponseCodes.RESPONSE_FAILED,
199                             OPERATION_FAILED);
200                 }
201                 ServicePathInputData servicePathInputDataAtoZ =
202                         ModelMappingUtils.rendererCreateServiceInputAToZ(serviceName, pathDescription);
203                 ServicePathInputData servicePathInputDataZtoA =
204                         ModelMappingUtils.rendererCreateServiceInputZToA(serviceName, pathDescription);
205                 // OLM turn down power
206                 try {
207                     LOG.debug("Turning down power on A-to-Z path");
208                     sendNotifications(ServicePathNotificationTypes.ServiceDelete,
209                             input.getServiceName(), RpcStatusEx.Pending, "Turning down power on A-to-Z path");
210                     ServicePowerTurndownOutput atozPowerTurndownOutput = olmPowerTurndown(servicePathInputDataAtoZ);
211                     // TODO add some flag rather than string
212                     if (FAILED.equals(atozPowerTurndownOutput.getResult())) {
213                         LOG.error("Service power turndown failed on A-to-Z path for service {}!", serviceName);
214                         sendNotifications(ServicePathNotificationTypes.ServiceDelete,
215                                 input.getServiceName(), RpcStatusEx.Failed,
216                                 "Service power turndown failed on A-to-Z path for service");
217                         return ModelMappingUtils.createServiceDeleteResponse(ResponseCodes.RESPONSE_FAILED,
218                                 OPERATION_FAILED);
219                     }
220                     LOG.debug("Turning down power on Z-to-A path");
221                     sendNotifications(ServicePathNotificationTypes.ServiceDelete, input.getServiceName(),
222                             RpcStatusEx.Pending, "Turning down power on Z-to-A path");
223                     ServicePowerTurndownOutput ztoaPowerTurndownOutput = olmPowerTurndown(servicePathInputDataZtoA);
224                     // TODO add some flag rather than string
225                     if (FAILED.equals(ztoaPowerTurndownOutput.getResult())) {
226                         LOG.error("Service power turndown failed on Z-to-A path for service {}!", serviceName);
227                         sendNotifications(ServicePathNotificationTypes.ServiceDelete,
228                                 input.getServiceName(), RpcStatusEx.Failed,
229                                 "Service power turndown failed on Z-to-A path for service");
230                         return ModelMappingUtils.createServiceDeleteResponse(ResponseCodes.RESPONSE_FAILED,
231                                 OPERATION_FAILED);
232                     }
233                 } catch (InterruptedException | ExecutionException | TimeoutException e) {
234                     LOG.error("Error while turning down power!", e);
235                     return ModelMappingUtils.createServiceDeleteResponse(ResponseCodes.RESPONSE_FAILED,
236                             OPERATION_FAILED);
237                 }
238                 // delete service path with renderer
239                 LOG.debug("Deleting service path via renderer");
240                 sendNotifications(ServicePathNotificationTypes.ServiceDelete, input.getServiceName(),
241                         RpcStatusEx.Pending, "Deleting service path via renderer");
242                 deviceRenderer.deleteServicePath(servicePathInputDataAtoZ.getServicePathInput());
243                 deviceRenderer.deleteServicePath(servicePathInputDataZtoA.getServicePathInput());
244                 networkModelWavelengthService.freeWavelengths(pathDescription);
245                 sendNotifications(ServicePathNotificationTypes.ServiceDelete, input.getServiceName(),
246                         RpcStatusEx.Successful, OPERATION_SUCCESSFUL);
247                 return ModelMappingUtils.createServiceDeleteResponse(ResponseCodes.RESPONSE_OK, OPERATION_SUCCESSFUL);
248             }
249         });
250
251
252     }
253
254     private ServicePowerTurndownOutput olmPowerTurndown(ServicePathInputData servicePathInputData)
255             throws InterruptedException, ExecutionException, TimeoutException {
256         LOG.debug("Turning down power on A-to-Z path");
257         Future<RpcResult<ServicePowerTurndownOutput>> powerTurndownFuture = this.olmService.servicePowerTurndown(
258                 new ServicePowerTurndownInputBuilder(servicePathInputData.getServicePathInput()).build());
259         return powerTurndownFuture.get(Timeouts.DATASTORE_READ, TimeUnit.MILLISECONDS).getResult();
260     }
261
262     private Optional<PathDescription> getPathDescriptionFromDatastore(String serviceName) {
263         InstanceIdentifier<PathDescription> pathDescriptionIID = InstanceIdentifier.create(ServicePathList.class)
264                 .child(ServicePaths.class, new ServicePathsKey(serviceName)).child(PathDescription.class);
265         ReadOnlyTransaction pathDescReadTx = this.dataBroker.newReadOnlyTransaction();
266         try {
267             LOG.debug("Getting path description for service {}", serviceName);
268             return pathDescReadTx.read(LogicalDatastoreType.OPERATIONAL, pathDescriptionIID)
269                     .get(Timeouts.DATASTORE_READ, TimeUnit.MILLISECONDS);
270         } catch (InterruptedException | ExecutionException | TimeoutException e) {
271             LOG.warn("Exception while getting path description from datastore {} for service {}!", pathDescriptionIID,
272                     serviceName, e);
273             return Optional.absent();
274         }
275     }
276
277     private List<DeviceRenderingResult> deviceRendering(RollbackProcessor rollbackProcessor,
278             ServicePathInputData servicePathDataAtoZ, ServicePathInputData servicePathDataZtoA) {
279         LOG.info("Rendering devices A-Z");
280         sendNotifications(ServicePathNotificationTypes.ServiceImplementationRequest,
281                 servicePathDataAtoZ.getServicePathInput().getServiceName(), RpcStatusEx.Pending,
282                 "Rendering devices A-Z");
283         ListenableFuture<DeviceRenderingResult> atozrenderingFuture =
284                 this.executor.submit(new DeviceRenderingTask(this.deviceRenderer, servicePathDataAtoZ,
285                         ServicePathDirection.A_TO_Z));
286
287         LOG.info("Rendering devices Z-A");
288         sendNotifications(ServicePathNotificationTypes.ServiceImplementationRequest,
289                 servicePathDataAtoZ.getServicePathInput().getServiceName(), RpcStatusEx.Pending,
290                 "Rendering devices A-Z");
291         ListenableFuture<DeviceRenderingResult> ztoarenderingFuture =
292                 this.executor.submit(new DeviceRenderingTask(this.deviceRenderer, servicePathDataZtoA,
293                         ServicePathDirection.Z_TO_A));
294         ListenableFuture<List<DeviceRenderingResult>> renderingCombinedFuture =
295                 Futures.allAsList(atozrenderingFuture, ztoarenderingFuture);
296
297         List<DeviceRenderingResult> renderingResults = new ArrayList<>(2);
298         try {
299             LOG.info("Waiting for A-Z and Z-A device renderers ...");
300             renderingResults = renderingCombinedFuture.get(Timeouts.RENDERING_TIMEOUT, TimeUnit.MILLISECONDS);
301         } catch (InterruptedException | ExecutionException | TimeoutException e) {
302             LOG.warn("Device rendering was not successful! Rendering will be rolled back.", e);
303             sendNotifications(ServicePathNotificationTypes.ServiceImplementationRequest,
304                     servicePathDataAtoZ.getServicePathInput().getServiceName(), RpcStatusEx.Pending,
305                     "Device rendering was not successful! Rendering will be rolled back.");
306             //FIXME we can't do rollback here, because we don't have rendering results.
307             //rollbackProcessor.addTask(new DeviceRenderingRollbackTask("AtoZDeviceTask", true));
308             //rollbackProcessor.addTask(new DeviceRenderingRollbackTask("ZtoADeviceTask", true));
309             return renderingResults;
310         }
311
312         rollbackProcessor.addTask(new DeviceRenderingRollbackTask("AtoZDeviceTask",
313                 ! renderingResults.get(0).isSuccess(), renderingResults.get(0).getRenderedNodeInterfaces(),
314                 this.deviceRenderer));
315         rollbackProcessor.addTask(new DeviceRenderingRollbackTask("ZtoADeviceTask",
316                 ! renderingResults.get(1).isSuccess(), renderingResults.get(1).getRenderedNodeInterfaces(),
317                 this.deviceRenderer));
318         return renderingResults;
319     }
320
321     private void olmPowerSetup(RollbackProcessor rollbackProcessor, ServicePowerSetupInput powerSetupInputAtoZ,
322             ServicePowerSetupInput powerSetupInputZtoA) {
323         LOG.info("Olm power setup A-Z");
324         sendNotifications(ServicePathNotificationTypes.ServiceImplementationRequest,
325                 powerSetupInputAtoZ.getServiceName(), RpcStatusEx.Pending, "Olm power setup A-Z");
326         ListenableFuture<OLMRenderingResult> olmPowerSetupFutureAtoZ
327                 = this.executor.submit(new OlmPowerSetupTask(this.olmService, powerSetupInputAtoZ));
328
329         LOG.info("OLM power setup Z-A");
330         sendNotifications(ServicePathNotificationTypes.ServiceImplementationRequest,
331                 powerSetupInputAtoZ.getServiceName(), RpcStatusEx.Pending, "Olm power setup Z-A");
332         ListenableFuture<OLMRenderingResult> olmPowerSetupFutureZtoA
333                 = this.executor.submit(new OlmPowerSetupTask(this.olmService, powerSetupInputZtoA));
334         ListenableFuture<List<OLMRenderingResult>> olmFutures =
335                 Futures.allAsList(olmPowerSetupFutureAtoZ, olmPowerSetupFutureZtoA);
336
337         List<OLMRenderingResult> olmResults;
338         try {
339             LOG.info("Waiting for A-Z and Z-A OLM power setup ...");
340             olmResults = olmFutures.get(Timeouts.OLM_TIMEOUT, TimeUnit.MILLISECONDS);
341         } catch (InterruptedException | ExecutionException | TimeoutException e) {
342             LOG.warn("OLM power setup was not successful! Rendering and OLM will be rolled back.", e);
343             sendNotifications(ServicePathNotificationTypes.ServiceImplementationRequest,
344                     powerSetupInputAtoZ.getServiceName(), RpcStatusEx.Pending,
345                     "OLM power setup was not successful! Rendering and OLM will be rolled back.");
346             rollbackProcessor.addTask(new OlmPowerSetupRollbackTask("AtoZOLMTask", true,
347                     this.olmService, powerSetupInputAtoZ));
348             rollbackProcessor.addTask(new OlmPowerSetupRollbackTask("ZtoAOLMTask", true,
349                     this.olmService, powerSetupInputZtoA));
350             return;
351         }
352
353         rollbackProcessor.addTask(new OlmPowerSetupRollbackTask("AtoZOLMTask", ! olmResults.get(0).isSuccess(),
354                 this.olmService, powerSetupInputAtoZ));
355         rollbackProcessor.addTask(new OlmPowerSetupRollbackTask("ZtoAOLMTask", ! olmResults.get(1).isSuccess(),
356                 this.olmService, powerSetupInputZtoA));
357     }
358
359     private boolean isServiceActivated(String nodeId, String tpId) {
360         LOG.info("Starting service activation test on node {} and tp {}", nodeId, tpId);
361         for (int i = 0; i < 3; i++) {
362             List<Measurements> measurements = getMeasurements(nodeId, tpId);
363             if ((measurements != null) && verifyPreFecBer(measurements)) {
364                 return true;
365             } else if (measurements == null) {
366                 LOG.warn("Device {} is not reporting PreFEC on TP: {}", nodeId, tpId);
367                 return true;
368             } else {
369                 try {
370                     Thread.sleep(Timeouts.SERVICE_ACTIVATION_TEST_RETRY_TIME);
371                 } catch (InterruptedException ex) {
372                     Thread.currentThread().interrupt();
373                 }
374             }
375         }
376         LOG.error("Service activation test failed on node {} and termination point {}!", nodeId, tpId);
377         return false;
378     }
379
380     private List<Measurements> getMeasurements(String nodeId, String tp) {
381         GetPmInputBuilder getPmIpBldr = new GetPmInputBuilder();
382         getPmIpBldr.setNodeId(nodeId);
383         getPmIpBldr.setGranularity(PmGranularity._15min);
384         ResourceIdentifierBuilder rsrcBldr = new ResourceIdentifierBuilder();
385         rsrcBldr.setResourceName(tp + "-OTU");
386         getPmIpBldr.setResourceIdentifier(rsrcBldr.build());
387         getPmIpBldr.setResourceType(ResourceTypeEnum.Interface);
388
389         try {
390             Future<RpcResult<GetPmOutput>> getPmFuture = this.olmService.getPm(getPmIpBldr.build());
391             RpcResult<GetPmOutput> getPmRpcResult = getPmFuture.get();
392             GetPmOutput getPmOutput = getPmRpcResult.getResult();
393             if ((getPmOutput != null) && (getPmOutput.getNodeId() != null)) {
394                 LOG.info("successfully finished calling OLM's get PM");
395                 return getPmOutput.getMeasurements(); // may return null
396
397             } else {
398                 LOG.warn("OLM's get PM failed for node {} and tp {}", nodeId, tp);
399             }
400
401         } catch (ExecutionException | InterruptedException e) {
402             LOG.warn("Error occurred while getting PM for node {} and tp {}", nodeId, tp, e);
403         }
404         return null;
405     }
406
407
408     private boolean verifyPreFecBer(List<Measurements> measurements) {
409         double preFecCorrectedErrors = Double.MIN_VALUE;
410         double fecUncorrectableBlocks = Double.MIN_VALUE;
411
412         for (Measurements measurement : measurements) {
413             if (measurement.getPmparameterName().equals("preFECCorrectedErrors")) {
414                 preFecCorrectedErrors = Double.parseDouble(measurement.getPmparameterValue());
415             }
416             if (measurement.getPmparameterName().equals("FECUncorrectableBlocks")) {
417                 fecUncorrectableBlocks = Double.parseDouble(measurement.getPmparameterValue());
418             }
419         }
420
421         LOG.info("Measurements: preFECCorrectedErrors = {}; FECUncorrectableBlocks = {}", preFecCorrectedErrors,
422                 fecUncorrectableBlocks);
423
424         if (fecUncorrectableBlocks > Double.MIN_VALUE) {
425             LOG.error("Data has uncorrectable errors, BER test failed");
426             return false;
427         } else {
428             double numOfBitsPerSecond = 112000000000d;
429             double threshold = 0.00002d;
430             double result = preFecCorrectedErrors / numOfBitsPerSecond;
431             LOG.info("PreFEC value is {}", Double.toString(result));
432             return result <= threshold;
433         }
434     }
435
436 }