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