Bump upstream dependencies to Ca
[transportpce.git] / servicehandler / src / main / java / org / opendaylight / transportpce / servicehandler / listeners / ServiceListener.java
1 /*
2  * Copyright © 2021 Orange 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.servicehandler.listeners;
9
10 import com.google.common.util.concurrent.ListenableFuture;
11 import com.google.common.util.concurrent.MoreExecutors;
12 import java.util.HashMap;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.Optional;
16 import java.util.concurrent.ExecutionException;
17 import java.util.concurrent.ScheduledExecutorService;
18 import java.util.concurrent.ScheduledThreadPoolExecutor;
19 import java.util.concurrent.TimeUnit;
20 import org.opendaylight.mdsal.binding.api.DataObjectModification;
21 import org.opendaylight.mdsal.binding.api.DataTreeChangeListener;
22 import org.opendaylight.mdsal.binding.api.DataTreeModification;
23 import org.opendaylight.mdsal.binding.api.NotificationPublishService;
24 import org.opendaylight.mdsal.binding.api.RpcService;
25 import org.opendaylight.transportpce.common.ResponseCodes;
26 import org.opendaylight.transportpce.servicehandler.ServiceInput;
27 import org.opendaylight.transportpce.servicehandler.service.ServiceDataStoreOperations;
28 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev230526.Restorable;
29 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev230526.RpcActions;
30 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev230526.routing.metric.RoutingMetric;
31 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev230526.sdnc.request.header.SdncRequestHeaderBuilder;
32 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev230526.service.resiliency.ServiceResiliency;
33 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.state.types.rev191129.State;
34 import org.opendaylight.yang.gen.v1.http.org.openroadm.equipment.states.types.rev191129.AdminStates;
35 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.ServiceCreate;
36 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.ServiceCreateInputBuilder;
37 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.ServiceCreateOutput;
38 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.ServiceDelete;
39 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.ServiceDeleteInputBuilder;
40 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.ServiceDeleteOutput;
41 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.ServiceReroute;
42 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.ServiceRerouteInput;
43 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.ServiceRerouteInputBuilder;
44 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.ServiceRerouteOutput;
45 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.service.create.input.ServiceAEndBuilder;
46 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.service.create.input.ServiceZEndBuilder;
47 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.service.delete.input.ServiceDeleteReqInfo;
48 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.service.delete.input.ServiceDeleteReqInfoBuilder;
49 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.service.list.Services;
50 import org.opendaylight.yang.gen.v1.nbi.notifications.rev230728.PublishNotificationAlarmService;
51 import org.opendaylight.yang.gen.v1.nbi.notifications.rev230728.PublishNotificationAlarmServiceBuilder;
52 import org.opendaylight.yangtools.yang.common.RpcResult;
53 import org.osgi.service.component.annotations.Activate;
54 import org.osgi.service.component.annotations.Component;
55 import org.osgi.service.component.annotations.Reference;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
58
59 @Component
60 public class ServiceListener implements DataTreeChangeListener<Services> {
61
62     private static final Logger LOG = LoggerFactory.getLogger(ServiceListener.class);
63     private static final String PUBLISHER = "ServiceListener";
64     private final RpcService rpcService;
65     private ServiceDataStoreOperations serviceDataStoreOperations;
66     private NotificationPublishService notificationPublishService;
67     private Map<String, ServiceInput> mapServiceInputReroute;
68     private final ScheduledExecutorService executor;
69
70     @Activate
71     public ServiceListener(@Reference RpcService rpcService,
72             @Reference ServiceDataStoreOperations serviceDataStoreOperations,
73             @Reference NotificationPublishService notificationPublishService) {
74         this.rpcService = rpcService;
75         this.serviceDataStoreOperations = serviceDataStoreOperations;
76         this.notificationPublishService = notificationPublishService;
77         this.executor = MoreExecutors.getExitingScheduledExecutorService(new ScheduledThreadPoolExecutor(4));
78         mapServiceInputReroute = new HashMap<>();
79     }
80
81     @Override
82     public void onDataTreeChanged(List<DataTreeModification<Services>> changes) {
83         LOG.info("onDataTreeChanged - {}", this.getClass().getSimpleName());
84         for (DataTreeModification<Services> change : changes) {
85             DataObjectModification<Services> rootService = change.getRootNode();
86             if (rootService.dataBefore() == null) {
87                 continue;
88             }
89             String serviceInputName = rootService.dataBefore().key().getServiceName();
90             switch (rootService.modificationType()) {
91                 case DELETE:
92                     LOG.info("Service {} correctly deleted from controller", serviceInputName);
93                     if (mapServiceInputReroute.get(serviceInputName) != null) {
94                         serviceRerouteStep2(serviceInputName);
95                     }
96                     break;
97                 case WRITE:
98                     Services inputBefore = rootService.dataBefore();
99                     Services inputAfter = rootService.dataAfter();
100                     if (inputBefore.getOperationalState() == State.InService
101                             && inputAfter.getOperationalState() == State.OutOfService) {
102                         LOG.info("Service {} is becoming outOfService", serviceInputName);
103                         sendNbiNotification(new PublishNotificationAlarmServiceBuilder()
104                                 .setServiceName(inputAfter.getServiceName())
105                                 .setConnectionType(inputAfter.getConnectionType())
106                                 .setMessage("The service is now outOfService")
107                                 .setOperationalState(State.OutOfService)
108                                 .setPublisherName(PUBLISHER)
109                                 .build());
110                         if (inputAfter.getAdministrativeState() == AdminStates.InService
111                                 && inputAfter.getServiceResiliency() != null
112                                 && inputAfter.getServiceResiliency().getResiliency() != null
113                                 && inputAfter.getServiceResiliency().getResiliency().equals(Restorable.VALUE)) {
114                             LOG.info("Attempting to reroute the service '{}'...", serviceInputName);
115                             if (!serviceRerouteCheck(serviceInputName, inputAfter.getServiceResiliency(),
116                                     inputAfter.getRoutingMetric())) {
117                                 LOG.info("No other path available, cancelling reroute process of service '{}'...",
118                                         serviceInputName);
119                                 continue;
120                             }
121                             mapServiceInputReroute.put(serviceInputName, null);
122                             if (inputAfter.getServiceResiliency().getHoldoffTime() != null) {
123                                 LOG.info("Waiting hold off time before rerouting...");
124                                 executor.schedule(
125                                         () -> {
126                                             if (mapServiceInputReroute.containsKey(serviceInputName)
127                                                     && mapServiceInputReroute.get(serviceInputName) == null) {
128                                                 serviceRerouteStep1(serviceInputName);
129                                             } else {
130                                                 LOG.info("Cancelling reroute process of service '{}'...",
131                                                         serviceInputName);
132                                             }
133                                         },
134                                         Long.parseLong(String.valueOf(inputAfter.getServiceResiliency()
135                                                 .getHoldoffTime())),
136                                         TimeUnit.MILLISECONDS);
137                             } else {
138                                 serviceRerouteStep1(serviceInputName);
139                             }
140                         }
141                     } else if (inputAfter.getAdministrativeState() == AdminStates.InService
142                             && inputBefore.getOperationalState() == State.OutOfService
143                             && inputAfter.getOperationalState() == State.InService) {
144                         LOG.info("Service {} is becoming InService", serviceInputName);
145                         sendNbiNotification(new PublishNotificationAlarmServiceBuilder()
146                                 .setServiceName(inputAfter.getServiceName())
147                                 .setConnectionType(inputAfter.getConnectionType())
148                                 .setMessage("The service is now inService")
149                                 .setOperationalState(State.InService)
150                                 .setPublisherName(PUBLISHER)
151                                 .build());
152                         if (mapServiceInputReroute.containsKey(serviceInputName)
153                                 && mapServiceInputReroute.get(serviceInputName) == null) {
154                             mapServiceInputReroute.remove(serviceInputName);
155                         }
156                     }
157                     break;
158                 default:
159                     LOG.debug("Unknown modification type {}", rootService.modificationType().name());
160                     break;
161             }
162         }
163     }
164
165     /**
166      * First step of the reroute : apply a service-delete RPC to the service.
167      *
168      * @param serviceNameToReroute Name of the service
169      */
170     private void serviceRerouteStep1(String serviceNameToReroute) {
171         mapServiceInputReroute.remove(serviceNameToReroute);
172         Optional<Services> serviceOpt = serviceDataStoreOperations.getService(serviceNameToReroute);
173         if (serviceOpt.isEmpty()) {
174             LOG.warn("Service '{}' does not exist in datastore", serviceNameToReroute);
175             return;
176         }
177         Services service = serviceOpt.orElseThrow();
178         ListenableFuture<RpcResult<ServiceDeleteOutput>> res = rpcService.getRpc(ServiceDelete.class).invoke(
179                 new ServiceDeleteInputBuilder()
180                         .setSdncRequestHeader(new SdncRequestHeaderBuilder(service.getSdncRequestHeader())
181                                 .setRpcAction(RpcActions.ServiceDelete)
182                                 .build())
183                         .setServiceDeleteReqInfo(new ServiceDeleteReqInfoBuilder()
184                                 .setServiceName(serviceNameToReroute)
185                                 .setTailRetention(ServiceDeleteReqInfo.TailRetention.No)
186                                 .build())
187                         .build());
188         try {
189             String httpResponseCode = res.get().getResult().getConfigurationResponseCommon().getResponseCode();
190             if (httpResponseCode.equals(ResponseCodes.RESPONSE_OK)) {
191                 mapServiceInputReroute.put(serviceNameToReroute, new ServiceInput(
192                         new ServiceCreateInputBuilder()
193                                 .setServiceName(serviceNameToReroute)
194                                 .setCommonId(service.getCommonId())
195                                 .setConnectionType(service.getConnectionType())
196                                 .setServiceAEnd(new ServiceAEndBuilder(service.getServiceAEnd()).build())
197                                 .setServiceZEnd(new ServiceZEndBuilder(service.getServiceZEnd()).build())
198                                 .setHardConstraints(service.getHardConstraints())
199                                 .setSoftConstraints(service.getSoftConstraints())
200                                 .setSdncRequestHeader(service.getSdncRequestHeader())
201                                 .setCustomer(service.getCustomer())
202                                 .setCustomerContact(service.getCustomerContact())
203                                 .setServiceResiliency(service.getServiceResiliency())
204                                 .setDueDate(service.getDueDate())
205                                 .setOperatorContact(service.getOperatorContact())
206                                 .build()));
207                 LOG.info("ServiceRerouteStep1 (deletion of the service) in progress");
208             } else {
209                 LOG.warn("ServiceRerouteStep1 (deletion of the service) failed '{}' http code ", httpResponseCode);
210             }
211         } catch (ExecutionException | InterruptedException e) {
212             LOG.warn("ServiceRerouteStep1 FAILED ! ", e);
213         }
214     }
215
216     /**
217      * Second step of the reroute : apply a service-create RPC. This method is called after the first step of reroute
218      * when the service has been successfully deleted.
219      *
220      * @param serviceNameToReroute Name of the service
221      */
222     private void serviceRerouteStep2(String serviceNameToReroute) {
223         ListenableFuture<RpcResult<ServiceCreateOutput>> res = rpcService.getRpc(ServiceCreate.class).invoke(
224                 mapServiceInputReroute.get(serviceNameToReroute).getServiceCreateInput());
225         try {
226             String httpResponseCode = res.get().getResult().getConfigurationResponseCommon().getResponseCode();
227             if (httpResponseCode.equals(ResponseCodes.RESPONSE_OK)) {
228                 LOG.info("ServiceRerouteStep2 (creation of the new service) in progress");
229             } else {
230                 LOG.warn("ServiceRerouteStep2 (creation of the new service) failed '{}' http code ", httpResponseCode);
231             }
232         } catch (ExecutionException | InterruptedException e) {
233             LOG.warn("ServiceRerouteStep2 FAILED ! ", e);
234         }
235         mapServiceInputReroute.remove(serviceNameToReroute);
236     }
237
238     /**
239      * Prior to the reroute steps: check that an alternative route of the service is possible.
240      *
241      * @param serviceNameToReroute Name of the service
242      * @param serviceResiliency Resiliency of the service
243      * @param routingMetric Metric of the routing
244      */
245     private boolean serviceRerouteCheck(String serviceNameToReroute, ServiceResiliency serviceResiliency,
246                                         RoutingMetric routingMetric) {
247         ServiceRerouteInput serviceRerouteInput = new ServiceRerouteInputBuilder()
248                 .setServiceName(serviceNameToReroute)
249                 .setServiceResiliency(serviceResiliency)
250                 .setRoutingMetric(routingMetric)
251                 .setSdncRequestHeader(new SdncRequestHeaderBuilder()
252                         .setRpcAction(RpcActions.ServiceReroute)
253                         .build())
254                 .build();
255         ListenableFuture<RpcResult<ServiceRerouteOutput>> res = rpcService.getRpc(ServiceReroute.class).invoke(
256                 serviceRerouteInput);
257         try {
258             return res.get().getResult().getConfigurationResponseCommon().getResponseCode()
259                     .equals(ResponseCodes.RESPONSE_OK);
260         } catch (ExecutionException | InterruptedException e) {
261             LOG.warn("ServiceRerouteCheck FAILED ! ", e);
262             return false;
263         }
264     }
265
266     /**
267      * Send notification to NBI notification in order to publish message.
268      *
269      * @param service PublishNotificationAlarmService
270      */
271     private void sendNbiNotification(PublishNotificationAlarmService service) {
272         try {
273             notificationPublishService.putNotification(service);
274         } catch (InterruptedException e) {
275             LOG.warn("Cannot send notification to nbi", e);
276             Thread.currentThread().interrupt();
277         }
278     }
279
280 }