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