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