2 * Copyright © 2021 Orange and others. All rights reserved.
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
8 package org.opendaylight.transportpce.servicehandler.listeners;
10 import com.google.common.util.concurrent.ListenableFuture;
11 import com.google.common.util.concurrent.MoreExecutors;
12 import java.util.Collection;
13 import java.util.HashMap;
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.transportpce.common.ResponseCodes;
25 import org.opendaylight.transportpce.pce.service.PathComputationService;
26 import org.opendaylight.transportpce.servicehandler.ServiceInput;
27 import org.opendaylight.transportpce.servicehandler.impl.ServicehandlerImpl;
28 import org.opendaylight.transportpce.servicehandler.service.ServiceDataStoreOperations;
29 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev211210.Restorable;
30 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev211210.RpcActions;
31 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev211210.routing.metric.RoutingMetric;
32 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev211210.sdnc.request.header.SdncRequestHeaderBuilder;
33 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev211210.service.resiliency.ServiceResiliency;
34 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.state.types.rev191129.State;
35 import org.opendaylight.yang.gen.v1.http.org.openroadm.equipment.states.types.rev191129.AdminStates;
36 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.ServiceCreateInputBuilder;
37 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.ServiceCreateOutput;
38 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.ServiceDeleteInputBuilder;
39 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.ServiceDeleteOutput;
40 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.ServiceRerouteInput;
41 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.ServiceRerouteInputBuilder;
42 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.ServiceRerouteOutput;
43 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.service.create.input.ServiceAEndBuilder;
44 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.service.create.input.ServiceZEndBuilder;
45 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.service.delete.input.ServiceDeleteReqInfo;
46 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.service.delete.input.ServiceDeleteReqInfoBuilder;
47 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.service.list.Services;
48 import org.opendaylight.yang.gen.v1.nbi.notifications.rev211013.PublishNotificationAlarmService;
49 import org.opendaylight.yang.gen.v1.nbi.notifications.rev211013.PublishNotificationAlarmServiceBuilder;
50 import org.opendaylight.yangtools.yang.common.RpcResult;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
54 public class ServiceListener implements DataTreeChangeListener<Services> {
56 private static final Logger LOG = LoggerFactory.getLogger(ServiceListener.class);
57 private static final String PUBLISHER = "ServiceListener";
58 private ServicehandlerImpl servicehandlerImpl;
59 private ServiceDataStoreOperations serviceDataStoreOperations;
60 private NotificationPublishService notificationPublishService;
61 private PathComputationService pathComputationService;
62 private Map<String, ServiceInput> mapServiceInputReroute;
63 private final ScheduledExecutorService executor;
65 public ServiceListener(ServicehandlerImpl servicehandlerImpl, ServiceDataStoreOperations serviceDataStoreOperations,
66 NotificationPublishService notificationPublishService) {
67 this.servicehandlerImpl = servicehandlerImpl;
68 this.notificationPublishService = notificationPublishService;
69 this.serviceDataStoreOperations = serviceDataStoreOperations;
70 this.executor = MoreExecutors.getExitingScheduledExecutorService(new ScheduledThreadPoolExecutor(4));
71 mapServiceInputReroute = new HashMap<>();
75 public void onDataTreeChanged(Collection<DataTreeModification<Services>> changes) {
76 LOG.info("onDataTreeChanged - {}", this.getClass().getSimpleName());
77 for (DataTreeModification<Services> change : changes) {
78 DataObjectModification<Services> rootService = change.getRootNode();
79 if (rootService.getDataBefore() == null) {
82 String serviceInputName = rootService.getDataBefore().key().getServiceName();
83 switch (rootService.getModificationType()) {
85 LOG.info("Service {} correctly deleted from controller", serviceInputName);
86 if (mapServiceInputReroute.get(serviceInputName) != null) {
87 serviceRerouteStep2(serviceInputName);
91 Services inputBefore = rootService.getDataBefore();
92 Services inputAfter = rootService.getDataAfter();
93 if (inputBefore.getOperationalState() == State.InService
94 && inputAfter.getOperationalState() == State.OutOfService) {
95 LOG.info("Service {} is becoming outOfService", serviceInputName);
96 sendNbiNotification(new PublishNotificationAlarmServiceBuilder()
97 .setServiceName(inputAfter.getServiceName())
98 .setConnectionType(inputAfter.getConnectionType())
99 .setMessage("The service is now outOfService")
100 .setOperationalState(State.OutOfService)
101 .setPublisherName(PUBLISHER)
103 if (inputAfter.getAdministrativeState() == AdminStates.InService
104 && inputAfter.getServiceResiliency() != null
105 && inputAfter.getServiceResiliency().getResiliency() != null
106 && inputAfter.getServiceResiliency().getResiliency().equals(Restorable.VALUE)) {
107 LOG.info("Attempting to reroute the service '{}'...", serviceInputName);
108 if (!serviceRerouteCheck(serviceInputName, inputAfter.getServiceResiliency(),
109 inputAfter.getRoutingMetric())) {
110 LOG.info("No other path available, cancelling reroute process of service '{}'...",
114 mapServiceInputReroute.put(serviceInputName, null);
115 if (inputAfter.getServiceResiliency().getHoldoffTime() != null) {
116 LOG.info("Waiting hold off time before rerouting...");
119 if (mapServiceInputReroute.containsKey(serviceInputName)
120 && mapServiceInputReroute.get(serviceInputName) == null) {
121 serviceRerouteStep1(serviceInputName);
123 LOG.info("Cancelling reroute process of service '{}'...",
127 Long.parseLong(String.valueOf(inputAfter.getServiceResiliency()
129 TimeUnit.MILLISECONDS);
131 serviceRerouteStep1(serviceInputName);
134 } else if (inputAfter.getAdministrativeState() == AdminStates.InService
135 && inputBefore.getOperationalState() == State.OutOfService
136 && inputAfter.getOperationalState() == State.InService) {
137 LOG.info("Service {} is becoming InService", serviceInputName);
138 sendNbiNotification(new PublishNotificationAlarmServiceBuilder()
139 .setServiceName(inputAfter.getServiceName())
140 .setConnectionType(inputAfter.getConnectionType())
141 .setMessage("The service is now inService")
142 .setOperationalState(State.InService)
143 .setPublisherName(PUBLISHER)
145 if (mapServiceInputReroute.containsKey(serviceInputName)
146 && mapServiceInputReroute.get(serviceInputName) == null) {
147 mapServiceInputReroute.remove(serviceInputName);
152 LOG.debug("Unknown modification type {}", rootService.getModificationType().name());
159 * First step of the reroute : apply a service-delete RPC to the service.
161 * @param serviceNameToReroute Name of the service
163 private void serviceRerouteStep1(String serviceNameToReroute) {
164 mapServiceInputReroute.remove(serviceNameToReroute);
165 Optional<Services> serviceOpt = serviceDataStoreOperations.getService(serviceNameToReroute);
166 if (serviceOpt.isEmpty()) {
167 LOG.warn("Service '{}' does not exist in datastore", serviceNameToReroute);
170 Services service = serviceOpt.get();
171 ListenableFuture<RpcResult<ServiceDeleteOutput>> res = this.servicehandlerImpl.serviceDelete(
172 new ServiceDeleteInputBuilder()
173 .setSdncRequestHeader(new SdncRequestHeaderBuilder(service.getSdncRequestHeader())
174 .setRpcAction(RpcActions.ServiceDelete)
176 .setServiceDeleteReqInfo(new ServiceDeleteReqInfoBuilder()
177 .setServiceName(serviceNameToReroute)
178 .setTailRetention(ServiceDeleteReqInfo.TailRetention.No)
182 String httpResponseCode = res.get().getResult().getConfigurationResponseCommon().getResponseCode();
183 if (httpResponseCode.equals(ResponseCodes.RESPONSE_OK)) {
184 mapServiceInputReroute.put(serviceNameToReroute, new ServiceInput(
185 new ServiceCreateInputBuilder()
186 .setServiceName(serviceNameToReroute)
187 .setCommonId(service.getCommonId())
188 .setConnectionType(service.getConnectionType())
189 .setServiceAEnd(new ServiceAEndBuilder(service.getServiceAEnd()).build())
190 .setServiceZEnd(new ServiceZEndBuilder(service.getServiceZEnd()).build())
191 .setHardConstraints(service.getHardConstraints())
192 .setSoftConstraints(service.getSoftConstraints())
193 .setSdncRequestHeader(service.getSdncRequestHeader())
194 .setCustomer(service.getCustomer())
195 .setCustomerContact(service.getCustomerContact())
196 .setServiceResiliency(service.getServiceResiliency())
197 .setDueDate(service.getDueDate())
198 .setOperatorContact(service.getOperatorContact())
200 LOG.info("ServiceRerouteStep1 (deletion of the service) in progress");
202 LOG.warn("ServiceRerouteStep1 (deletion of the service) failed '{}' http code ", httpResponseCode);
204 } catch (ExecutionException | InterruptedException e) {
205 LOG.warn("ServiceRerouteStep1 FAILED ! ", e);
210 * Second step of the reroute : apply a service-create RPC. This method is called after the first step of reroute
211 * when the service has been successfully deleted.
213 * @param serviceNameToReroute Name of the service
215 private void serviceRerouteStep2(String serviceNameToReroute) {
216 ListenableFuture<RpcResult<ServiceCreateOutput>> res = this.servicehandlerImpl.serviceCreate(
217 mapServiceInputReroute.get(serviceNameToReroute).getServiceCreateInput());
219 String httpResponseCode = res.get().getResult().getConfigurationResponseCommon().getResponseCode();
220 if (httpResponseCode.equals(ResponseCodes.RESPONSE_OK)) {
221 LOG.info("ServiceRerouteStep2 (creation of the new service) in progress");
223 LOG.warn("ServiceRerouteStep2 (creation of the new service) failed '{}' http code ", httpResponseCode);
225 } catch (ExecutionException | InterruptedException e) {
226 LOG.warn("ServiceRerouteStep2 FAILED ! ", e);
228 mapServiceInputReroute.remove(serviceNameToReroute);
232 * Prior to the reroute steps: check that an alternative route of the service is possible.
234 * @param serviceNameToReroute Name of the service
235 * @param serviceResiliency Resiliency of the service
236 * @param routingMetric Metric of the routing
238 private boolean serviceRerouteCheck(String serviceNameToReroute, ServiceResiliency serviceResiliency,
239 RoutingMetric routingMetric) {
240 ServiceRerouteInput serviceRerouteInput = new ServiceRerouteInputBuilder()
241 .setServiceName(serviceNameToReroute)
242 .setServiceResiliency(serviceResiliency)
243 .setRoutingMetric(routingMetric)
244 .setSdncRequestHeader(new SdncRequestHeaderBuilder()
245 .setRpcAction(RpcActions.ServiceReroute)
248 ListenableFuture<RpcResult<ServiceRerouteOutput>> res = this.servicehandlerImpl.serviceReroute(
249 serviceRerouteInput);
251 return res.get().getResult().getConfigurationResponseCommon().getResponseCode()
252 .equals(ResponseCodes.RESPONSE_OK);
253 } catch (ExecutionException | InterruptedException e) {
254 LOG.warn("ServiceRerouteCheck FAILED ! ", e);
260 * Send notification to NBI notification in order to publish message.
262 * @param service PublishNotificationAlarmService
264 private void sendNbiNotification(PublishNotificationAlarmService service) {
266 notificationPublishService.putNotification(service);
267 } catch (InterruptedException e) {
268 LOG.warn("Cannot send notification to nbi", e);
269 Thread.currentThread().interrupt();