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