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.HashMap;
13 import java.util.List;
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;
60 public class ServiceListener implements DataTreeChangeListener<Services> {
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;
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<>();
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) {
89 String serviceInputName = rootService.dataBefore().key().getServiceName();
90 switch (rootService.modificationType()) {
92 LOG.info("Service {} correctly deleted from controller", serviceInputName);
93 if (mapServiceInputReroute.get(serviceInputName) != null) {
94 serviceRerouteStep2(serviceInputName);
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)
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 '{}'...",
121 mapServiceInputReroute.put(serviceInputName, null);
122 if (inputAfter.getServiceResiliency().getHoldoffTime() != null) {
123 LOG.info("Waiting hold off time before rerouting...");
126 if (mapServiceInputReroute.containsKey(serviceInputName)
127 && mapServiceInputReroute.get(serviceInputName) == null) {
128 serviceRerouteStep1(serviceInputName);
130 LOG.info("Cancelling reroute process of service '{}'...",
134 Long.parseLong(String.valueOf(inputAfter.getServiceResiliency()
136 TimeUnit.MILLISECONDS);
138 serviceRerouteStep1(serviceInputName);
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)
152 if (mapServiceInputReroute.containsKey(serviceInputName)
153 && mapServiceInputReroute.get(serviceInputName) == null) {
154 mapServiceInputReroute.remove(serviceInputName);
159 LOG.debug("Unknown modification type {}", rootService.modificationType().name());
166 * First step of the reroute : apply a service-delete RPC to the service.
168 * @param serviceNameToReroute Name of the service
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);
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)
183 .setServiceDeleteReqInfo(new ServiceDeleteReqInfoBuilder()
184 .setServiceName(serviceNameToReroute)
185 .setTailRetention(ServiceDeleteReqInfo.TailRetention.No)
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())
207 LOG.info("ServiceRerouteStep1 (deletion of the service) in progress");
209 LOG.warn("ServiceRerouteStep1 (deletion of the service) failed '{}' http code ", httpResponseCode);
211 } catch (ExecutionException | InterruptedException e) {
212 LOG.warn("ServiceRerouteStep1 FAILED ! ", e);
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.
220 * @param serviceNameToReroute Name of the service
222 private void serviceRerouteStep2(String serviceNameToReroute) {
223 ListenableFuture<RpcResult<ServiceCreateOutput>> res = rpcService.getRpc(ServiceCreate.class).invoke(
224 mapServiceInputReroute.get(serviceNameToReroute).getServiceCreateInput());
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");
230 LOG.warn("ServiceRerouteStep2 (creation of the new service) failed '{}' http code ", httpResponseCode);
232 } catch (ExecutionException | InterruptedException e) {
233 LOG.warn("ServiceRerouteStep2 FAILED ! ", e);
235 mapServiceInputReroute.remove(serviceNameToReroute);
239 * Prior to the reroute steps: check that an alternative route of the service is possible.
241 * @param serviceNameToReroute Name of the service
242 * @param serviceResiliency Resiliency of the service
243 * @param routingMetric Metric of the routing
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)
255 ListenableFuture<RpcResult<ServiceRerouteOutput>> res = rpcService.getRpc(ServiceReroute.class).invoke(
256 serviceRerouteInput);
258 return res.get().getResult().getConfigurationResponseCommon().getResponseCode()
259 .equals(ResponseCodes.RESPONSE_OK);
260 } catch (ExecutionException | InterruptedException e) {
261 LOG.warn("ServiceRerouteCheck FAILED ! ", e);
267 * Send notification to NBI notification in order to publish message.
269 * @param service PublishNotificationAlarmService
271 private void sendNbiNotification(PublishNotificationAlarmService service) {
273 notificationPublishService.putNotification(service);
274 } catch (InterruptedException e) {
275 LOG.warn("Cannot send notification to nbi", e);
276 Thread.currentThread().interrupt();