Add unit tests PCE RPC path-computation-reroute
[transportpce.git] / pce / src / main / java / org / opendaylight / transportpce / pce / service / PathComputationServiceImpl.java
1 /*
2  * Copyright © 2017 AT&T, Inc. 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.pce.service;
9
10 import com.google.common.util.concurrent.ListenableFuture;
11 import com.google.common.util.concurrent.ListeningExecutorService;
12 import com.google.common.util.concurrent.MoreExecutors;
13 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
14 import java.util.ArrayList;
15 import java.util.List;
16 import java.util.concurrent.Callable;
17 import java.util.concurrent.Executors;
18 import java.util.stream.Collectors;
19 import org.opendaylight.mdsal.binding.api.NotificationPublishService;
20 import org.opendaylight.transportpce.common.mapping.PortMapping;
21 import org.opendaylight.transportpce.common.network.NetworkTransactionService;
22 import org.opendaylight.transportpce.pce.PceComplianceCheck;
23 import org.opendaylight.transportpce.pce.PceComplianceCheckResult;
24 import org.opendaylight.transportpce.pce.PceSendingPceRPCs;
25 import org.opendaylight.transportpce.pce.gnpy.GnpyResult;
26 import org.opendaylight.transportpce.pce.gnpy.consumer.GnpyConsumer;
27 import org.opendaylight.yang.gen.v1.gnpy.path.rev220615.result.Response;
28 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.CancelResourceReserveInput;
29 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.CancelResourceReserveOutput;
30 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.CancelResourceReserveOutputBuilder;
31 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.PathComputationRequestInput;
32 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.PathComputationRequestInputBuilder;
33 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.PathComputationRequestOutput;
34 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.PathComputationRequestOutputBuilder;
35 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.PathComputationRerouteRequestInput;
36 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.PathComputationRerouteRequestOutput;
37 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.PathComputationRerouteRequestOutputBuilder;
38 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.ServicePathRpcResult;
39 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.ServicePathRpcResultBuilder;
40 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.gnpy.GnpyResponse;
41 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.gnpy.GnpyResponseBuilder;
42 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.gnpy.gnpy.response.response.type.NoPathCaseBuilder;
43 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.gnpy.gnpy.response.response.type.PathCaseBuilder;
44 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.path.computation.request.input.ServiceAEndBuilder;
45 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.path.computation.request.input.ServiceZEndBuilder;
46 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.path.performance.PathPropertiesBuilder;
47 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.path.performance.path.properties.PathMetric;
48 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.path.performance.path.properties.PathMetricBuilder;
49 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.service.path.rpc.result.PathDescription;
50 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.service.path.rpc.result.PathDescriptionBuilder;
51 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev211210.configuration.response.common.ConfigurationResponseCommonBuilder;
52 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev210705.path.description.AToZDirection;
53 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev210705.path.description.ZToADirection;
54 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev220118.RpcStatusEx;
55 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev220118.ServicePathNotificationTypes;
56 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev220118.response.parameters.sp.ResponseParametersBuilder;
57 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev220118.service.handler.header.ServiceHandlerHeaderBuilder;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
60
61 public class PathComputationServiceImpl implements PathComputationService {
62
63     private static final Logger LOG = LoggerFactory.getLogger(PathComputationServiceImpl.class);
64     private final NotificationPublishService notificationPublishService;
65     private NetworkTransactionService networkTransactionService;
66     private final ListeningExecutorService executor;
67     private ServicePathRpcResult notification = null;
68     private final GnpyConsumer gnpyConsumer;
69     private PortMapping portMapping;
70
71     public PathComputationServiceImpl(NetworkTransactionService networkTransactionService,
72                                       NotificationPublishService notificationPublishService,
73                                       GnpyConsumer gnpyConsumer, PortMapping portMapping) {
74         this.notificationPublishService = notificationPublishService;
75         this.networkTransactionService = networkTransactionService;
76         this.executor = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(5));
77         this.gnpyConsumer = gnpyConsumer;
78         this.portMapping = portMapping;
79     }
80
81     public void init() {
82         LOG.info("init ...");
83     }
84
85     public void close() {
86         LOG.info("close.");
87     }
88
89     @SuppressFBWarnings(
90         value = "UPM_UNCALLED_PRIVATE_METHOD",
91         justification = "false positive, this method is used by public method cancelResourceReserve")
92     private void sendNotifications(
93             ServicePathNotificationTypes servicePathNotificationTypes,
94             String serviceName,
95             RpcStatusEx rpcStatusEx,
96             String message,
97             PathDescription pathDescription) {
98         ServicePathRpcResultBuilder servicePathRpcResultBuilder =
99             new ServicePathRpcResultBuilder()
100                 .setNotificationType(servicePathNotificationTypes)
101                 .setServiceName(serviceName)
102                 .setStatus(rpcStatusEx)
103                 .setStatusMessage(message);
104         if (pathDescription != null) {
105             servicePathRpcResultBuilder.setPathDescription(pathDescription);
106         }
107         this.notification = servicePathRpcResultBuilder.build();
108         try {
109             notificationPublishService.putNotification(this.notification);
110         } catch (InterruptedException e) {
111             LOG.info("notification offer rejected: ", e);
112         }
113     }
114
115     @Override
116     public ListenableFuture<CancelResourceReserveOutput> cancelResourceReserve(CancelResourceReserveInput input) {
117         LOG.info("cancelResourceReserve");
118         return executor.submit(new Callable<CancelResourceReserveOutput>() {
119
120             @Override
121             public CancelResourceReserveOutput call() throws Exception {
122                 sendNotifications(
123                         ServicePathNotificationTypes.CancelResourceReserve,
124                         input.getServiceName(),
125                         RpcStatusEx.Pending,
126                         "Service compliant, submitting cancelResourceReserve Request ...",
127                         null);
128                 PceSendingPceRPCs sendingPCE = new PceSendingPceRPCs(gnpyConsumer);
129                 sendingPCE.cancelResourceReserve();
130                 LOG.info("in PathComputationServiceImpl : {}",
131                         Boolean.TRUE.equals(sendingPCE.getSuccess())
132                             ? "ResourceReserve cancelled !"
133                             : "Cancelling ResourceReserve failed !");
134                 sendNotifications(
135                         ServicePathNotificationTypes.CancelResourceReserve,
136                         input.getServiceName(),
137                         RpcStatusEx.Successful,
138                         "cancel Resource Reserve successful!",
139                         null);
140                 return new CancelResourceReserveOutputBuilder()
141                     .setConfigurationResponseCommon(
142                         new ConfigurationResponseCommonBuilder()
143                             .setAckFinalIndicator("Yes")
144                             .setRequestId(input.getServiceHandlerHeader().getRequestId())
145                             .setResponseCode("200")
146                             .setResponseMessage("")
147                             .build())
148                     .build();
149             }
150         });
151     }
152
153     @Override
154     public ListenableFuture<PathComputationRequestOutput> pathComputationRequest(PathComputationRequestInput input) {
155         LOG.debug("input parameters are : input = {}", input.toString());
156         return executor.submit(new Callable<PathComputationRequestOutput>() {
157
158             @Override
159             public PathComputationRequestOutput call() throws Exception {
160                 PathComputationRequestOutputBuilder output = new PathComputationRequestOutputBuilder();
161                 ConfigurationResponseCommonBuilder configurationResponseCommon =
162                         new ConfigurationResponseCommonBuilder();
163                 PceComplianceCheckResult check = PceComplianceCheck.check(input);
164                 if (!check.hasPassed()) {
165                     LOG.error("Path not calculated, service not compliant : {}", check.getMessage());
166                     sendNotifications(
167                         ServicePathNotificationTypes.PathComputationRequest,
168                         input.getServiceName(),
169                         RpcStatusEx.Failed,
170                         "Path not calculated, service not compliant",
171                         null);
172                     configurationResponseCommon
173                             .setAckFinalIndicator("Yes")
174                             .setRequestId(input.getServiceHandlerHeader().getRequestId())
175                             .setResponseCode("Path not calculated")
176                             .setResponseMessage(check.getMessage());
177                     return output
178                         .setConfigurationResponseCommon(configurationResponseCommon.build())
179                         .setResponseParameters(null)
180                         .build();
181                 }
182                 sendNotifications(
183                     ServicePathNotificationTypes.PathComputationRequest,
184                     input.getServiceName(),
185                     RpcStatusEx.Pending,
186                     "Service compliant, submitting pathComputation Request ...",
187                     null);
188                 PceSendingPceRPCs sendingPCE =
189                     new PceSendingPceRPCs(input, networkTransactionService, gnpyConsumer, portMapping);
190                 sendingPCE.pathComputation();
191                 String message = sendingPCE.getMessage();
192                 String responseCode = sendingPCE.getResponseCode();
193                 LOG.info("PCE response: {} {}", message, responseCode);
194
195                 //add the GNPy result
196                 GnpyResult gnpyAtoZ = sendingPCE.getGnpyAtoZ();
197                 GnpyResult gnpyZtoA = sendingPCE.getGnpyZtoA();
198                 List<GnpyResponse> listResponse = new ArrayList<>();
199                 if (gnpyAtoZ != null) {
200                     GnpyResponse respAtoZ = generateGnpyResponse(gnpyAtoZ.getResponse(),"A-to-Z");
201                     listResponse.add(respAtoZ);
202                 }
203                 if (gnpyZtoA != null) {
204                     GnpyResponse respZtoA = generateGnpyResponse(gnpyZtoA.getResponse(),"Z-to-A");
205                     listResponse.add(respZtoA);
206                 }
207                 output
208                     .setGnpyResponse(
209                         listResponse.stream()
210                             .collect(Collectors.toMap(GnpyResponse::key, gnpyResponse -> gnpyResponse)));
211
212                 PathDescriptionBuilder path = sendingPCE.getPathDescription();
213                 if (Boolean.FALSE.equals(sendingPCE.getSuccess()) || (path == null)) {
214                     sendNotifications(
215                         ServicePathNotificationTypes.PathComputationRequest,
216                         input.getServiceName(),
217                         RpcStatusEx.Failed,
218                         "Path not calculated",
219                         null);
220                     return output
221                         .setConfigurationResponseCommon(
222                             configurationResponseCommon
223                                 .setAckFinalIndicator("Yes")
224                                 .setRequestId(input.getServiceHandlerHeader().getRequestId())
225                                 .setResponseCode(responseCode)
226                                 .setResponseMessage(message)
227                                 .build())
228                         .build();
229                 }
230                 // Path calculator returned Success
231                 PathDescription pathDescription =
232                     new org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808
233                             .service.path.rpc.result.PathDescriptionBuilder()
234                         .setAToZDirection(path.getAToZDirection())
235                         .setZToADirection(path.getZToADirection())
236                         .build();
237                 sendNotifications(
238                     ServicePathNotificationTypes.PathComputationRequest,
239                     input.getServiceName(),
240                     RpcStatusEx.Successful,
241                     message,
242                     pathDescription);
243                 org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev220118
244                         .response.parameters.sp.response.parameters.PathDescription pathDescription1 =
245                     new org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev220118
246                             .response.parameters.sp.response.parameters.PathDescriptionBuilder()
247                         .setAToZDirection(path.getAToZDirection())
248                         .setZToADirection(path.getZToADirection())
249                         .build();
250                 output
251                     .setConfigurationResponseCommon(
252                         configurationResponseCommon
253                             .setAckFinalIndicator("Yes")
254                             .setRequestId(input.getServiceHandlerHeader().getRequestId())
255                             .setResponseCode(responseCode)
256                             .setResponseMessage(message)
257                             .build())
258                     .setResponseParameters(
259                         new ResponseParametersBuilder().setPathDescription(pathDescription1).build());
260                 //debug prints
261                 AToZDirection atoz = pathDescription.getAToZDirection();
262                 if ((atoz != null) && (atoz.getAToZ() != null)) {
263                     LOG.debug("Impl AtoZ Notification: [{}] elements in description", atoz.getAToZ().size());
264                     for (org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev210705
265                             .path.description.atoz.direction.AToZKey key : atoz.getAToZ().keySet()) {
266                         LOG.debug("Impl AtoZ Notification: [{}] {}", key, atoz.getAToZ().get(key));
267                     }
268                 }
269                 ZToADirection ztoa = pathDescription.getZToADirection();
270                 if ((ztoa != null) && (ztoa.getZToA() != null)) {
271                     LOG.debug("Impl ZtoA Notification: [{}] elements in description", ztoa.getZToA().size());
272                     for (org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev210705
273                             .path.description.ztoa.direction.ZToAKey key : ztoa.getZToA().keySet()) {
274                         LOG.debug("Impl ZtoA Notification: [{}] {}", key, ztoa.getZToA().get(key));
275                     }
276                 }
277                 return output.build();
278             }
279         });
280     }
281
282     @Override
283     public ListenableFuture<PathComputationRerouteRequestOutput> pathComputationRerouteRequest(
284             PathComputationRerouteRequestInput input) {
285         return executor.submit(() -> {
286             PathComputationRerouteRequestOutputBuilder output = new PathComputationRerouteRequestOutputBuilder();
287             ConfigurationResponseCommonBuilder configurationResponseCommon = new ConfigurationResponseCommonBuilder()
288                     .setRequestId("none");
289             PceComplianceCheckResult check = PceComplianceCheck.check(input);
290             if (!check.hasPassed()) {
291                 LOG.error("Path not calculated, path computation reroute request not compliant : {}",
292                         check.getMessage());
293                 configurationResponseCommon
294                         .setAckFinalIndicator("Yes")
295                         .setResponseCode("Path not calculated")
296                         .setResponseMessage(check.getMessage());
297                 return output
298                         .setConfigurationResponseCommon(configurationResponseCommon.build())
299                         .build();
300             }
301             PathComputationRequestInput pathComputationInput = new PathComputationRequestInputBuilder()
302                     .setServiceName("no_name")
303                     .setServiceHandlerHeader(new ServiceHandlerHeaderBuilder().setRequestId("none").build())
304                     .setServiceAEnd(new ServiceAEndBuilder(input.getServiceAEnd()).build())
305                     .setServiceZEnd(new ServiceZEndBuilder(input.getServiceZEnd()).build())
306                     .setHardConstraints(input.getHardConstraints())
307                     .setPceRoutingMetric(input.getPceRoutingMetric())
308                     .setResourceReserve(false)
309                     .setSoftConstraints(input.getSoftConstraints())
310                     .setRoutingMetric(input.getRoutingMetric())
311                     .build();
312             PceSendingPceRPCs sendingPCE = new PceSendingPceRPCs(pathComputationInput, networkTransactionService,
313                     gnpyConsumer, portMapping, input.getEndpoints());
314             sendingPCE.pathComputation();
315             String message = sendingPCE.getMessage();
316             String responseCode = sendingPCE.getResponseCode();
317             LOG.info("PCE response: {} {}", message, responseCode);
318             return output.setConfigurationResponseCommon(
319                     configurationResponseCommon
320                             .setAckFinalIndicator("Yes")
321                             .setResponseCode(responseCode)
322                             .setResponseMessage(message)
323                             .build())
324                     .build();
325         });
326     }
327
328     public GnpyResponse generateGnpyResponse(Response responseGnpy, String pathDir) {
329         if (responseGnpy == null) {
330             return new GnpyResponseBuilder()
331                 .setPathDir(pathDir)
332                 .setResponseType(null)
333                 .setFeasibility(true)
334                 .build();
335         }
336         if (responseGnpy.getResponseType()
337                 instanceof org.opendaylight.yang.gen.v1.gnpy.path.rev220615.result.response.response.type.NoPathCase) {
338             LOG.info("GNPy : path is not feasible");
339             org.opendaylight.yang.gen.v1.gnpy.path.rev220615.result.response.response.type.NoPathCase
340                     noPathGnpy =
341                 (org.opendaylight.yang.gen.v1.gnpy.path.rev220615.result.response.response.type.NoPathCase)
342                     responseGnpy.getResponseType();
343             return new GnpyResponseBuilder()
344                 .setPathDir(pathDir)
345                 .setResponseType(
346                     new NoPathCaseBuilder()
347                         .setNoPath(noPathGnpy.getNoPath())
348                         .build())
349                 .setFeasibility(false)
350                 .build();
351         }
352         if (responseGnpy.getResponseType()
353                 instanceof org.opendaylight.yang.gen.v1.gnpy.path.rev220615.result.response.response.type.PathCase) {
354             LOG.info("GNPy : path is feasible");
355             org.opendaylight.yang.gen.v1.gnpy.path.rev220615.result.response.response.type.PathCase
356                     pathCase =
357                 (org.opendaylight.yang.gen.v1.gnpy.path.rev220615.result.response.response.type.PathCase)
358                     responseGnpy.getResponseType();
359             List<org.opendaylight.yang.gen.v1.gnpy.path.rev220615.generic.path.properties.path.properties.PathMetric>
360                     pathMetricList =
361                 new ArrayList<>(pathCase.getPathProperties().getPathMetric().values());
362             List<PathMetric> gnpyPathMetricList = new ArrayList<>();
363             for (org.opendaylight.yang.gen.v1.gnpy.path.rev220615.generic.path.properties.path.properties.PathMetric
364                     pathMetricGnpy : pathMetricList) {
365                 gnpyPathMetricList.add(
366                     new PathMetricBuilder()
367                         .setMetricType(pathMetricGnpy.getMetricType())
368                         .setAccumulativeValue(pathMetricGnpy.getAccumulativeValue())
369                         .build());
370             }
371             return new GnpyResponseBuilder()
372                 .setPathDir(pathDir)
373                 .setResponseType(
374                     new PathCaseBuilder()
375                         .setPathProperties(
376                             new PathPropertiesBuilder()
377                                 .setPathMetric(gnpyPathMetricList.stream()
378                                     .collect(Collectors.toMap(PathMetric::key, pathMetric -> pathMetric)))
379                                 .build())
380                         .build())
381                 .setFeasibility(true)
382                 .build();
383         }
384         return new GnpyResponseBuilder()
385             .setPathDir(pathDir)
386             .setResponseType(null)
387             .setFeasibility(true)
388             .build();
389     }
390
391 }