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.servicehandler.ServiceInput;
26 import org.opendaylight.transportpce.servicehandler.impl.ServicehandlerImpl;
27 import org.opendaylight.transportpce.servicehandler.service.ServiceDataStoreOperations;
28 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev211210.Restorable;
29 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev211210.RpcActions;
30 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev211210.sdnc.request.header.SdncRequestHeaderBuilder;
31 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.state.types.rev191129.State;
32 import org.opendaylight.yang.gen.v1.http.org.openroadm.equipment.states.types.rev191129.AdminStates;
33 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.ServiceCreateInputBuilder;
34 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.ServiceCreateOutput;
35 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.ServiceDeleteInputBuilder;
36 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.ServiceDeleteOutput;
37 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.service.create.input.ServiceAEndBuilder;
38 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.service.create.input.ServiceZEndBuilder;
39 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.service.delete.input.ServiceDeleteReqInfo;
40 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.service.delete.input.ServiceDeleteReqInfoBuilder;
41 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.service.list.Services;
42 import org.opendaylight.yang.gen.v1.nbi.notifications.rev211013.PublishNotificationAlarmService;
43 import org.opendaylight.yang.gen.v1.nbi.notifications.rev211013.PublishNotificationAlarmServiceBuilder;
44 import org.opendaylight.yangtools.yang.common.RpcResult;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
48 public class ServiceListener implements DataTreeChangeListener<Services> {
50 private static final Logger LOG = LoggerFactory.getLogger(ServiceListener.class);
51 private static final String PUBLISHER = "ServiceListener";
52 private ServicehandlerImpl servicehandlerImpl;
53 private ServiceDataStoreOperations serviceDataStoreOperations;
54 private NotificationPublishService notificationPublishService;
55 private Map<String, ServiceInput> mapServiceInputReroute;
56 private final ScheduledExecutorService executor;
58 public ServiceListener(ServicehandlerImpl servicehandlerImpl, ServiceDataStoreOperations serviceDataStoreOperations,
59 NotificationPublishService notificationPublishService) {
60 this.servicehandlerImpl = servicehandlerImpl;
61 this.notificationPublishService = notificationPublishService;
62 this.serviceDataStoreOperations = serviceDataStoreOperations;
63 this.executor = MoreExecutors.getExitingScheduledExecutorService(new ScheduledThreadPoolExecutor(4));
64 mapServiceInputReroute = new HashMap<>();
68 public void onDataTreeChanged(Collection<DataTreeModification<Services>> changes) {
69 LOG.info("onDataTreeChanged - {}", this.getClass().getSimpleName());
70 for (DataTreeModification<Services> change : changes) {
71 DataObjectModification<Services> rootService = change.getRootNode();
72 if (rootService.getDataBefore() == null) {
75 String serviceInputName = rootService.getDataBefore().key().getServiceName();
76 switch (rootService.getModificationType()) {
78 LOG.info("Service {} correctly deleted from controller", serviceInputName);
79 if (mapServiceInputReroute.get(serviceInputName) != null) {
80 serviceRerouteStep2(serviceInputName);
84 Services inputBefore = rootService.getDataBefore();
85 Services inputAfter = rootService.getDataAfter();
86 if (inputBefore.getOperationalState() == State.InService
87 && inputAfter.getOperationalState() == State.OutOfService) {
88 LOG.info("Service {} is becoming outOfService", serviceInputName);
89 sendNbiNotification(new PublishNotificationAlarmServiceBuilder()
90 .setServiceName(inputAfter.getServiceName())
91 .setConnectionType(inputAfter.getConnectionType())
92 .setMessage("The service is now outOfService")
93 .setOperationalState(State.OutOfService)
94 .setPublisherName(PUBLISHER)
96 if (inputAfter.getAdministrativeState() == AdminStates.InService
97 && inputAfter.getServiceResiliency() != null
98 && inputAfter.getServiceResiliency().getResiliency() != null
99 && inputAfter.getServiceResiliency().getResiliency().equals(Restorable.VALUE)) {
100 LOG.info("Attempting to reroute the service '{}'...", serviceInputName);
101 // It is used for hold off time purposes
102 mapServiceInputReroute.put(serviceInputName, null);
103 if (inputAfter.getServiceResiliency().getHoldoffTime() != null) {
104 LOG.info("Waiting hold off time before rerouting...");
107 if (mapServiceInputReroute.containsKey(serviceInputName)
108 && mapServiceInputReroute.get(serviceInputName) == null) {
109 serviceRerouteStep1(serviceInputName);
111 LOG.info("Cancelling rerouting for service '{}'...", serviceInputName);
114 Long.parseLong(String.valueOf(inputAfter.getServiceResiliency()
116 TimeUnit.MILLISECONDS);
118 serviceRerouteStep1(serviceInputName);
121 } else if (inputAfter.getAdministrativeState() == AdminStates.InService
122 && inputBefore.getOperationalState() == State.OutOfService
123 && inputAfter.getOperationalState() == State.InService) {
124 LOG.info("Service {} is becoming InService", serviceInputName);
125 sendNbiNotification(new PublishNotificationAlarmServiceBuilder()
126 .setServiceName(inputAfter.getServiceName())
127 .setConnectionType(inputAfter.getConnectionType())
128 .setMessage("The service is now inService")
129 .setOperationalState(State.InService)
130 .setPublisherName(PUBLISHER)
132 if (mapServiceInputReroute.containsKey(serviceInputName)
133 && mapServiceInputReroute.get(serviceInputName) == null) {
134 mapServiceInputReroute.remove(serviceInputName);
139 LOG.debug("Unknown modification type {}", rootService.getModificationType().name());
146 * First step of the reroute : apply a service-delete RPC to the service.
148 * @param serviceNameToReroute Name of the service
150 private void serviceRerouteStep1(String serviceNameToReroute) {
151 mapServiceInputReroute.remove(serviceNameToReroute);
152 Optional<Services> serviceOpt = serviceDataStoreOperations.getService(serviceNameToReroute);
153 if (serviceOpt.isEmpty()) {
154 LOG.warn("Service '{}' does not exist in datastore", serviceNameToReroute);
157 Services service = serviceOpt.get();
158 ListenableFuture<RpcResult<ServiceDeleteOutput>> res = this.servicehandlerImpl.serviceDelete(
159 new ServiceDeleteInputBuilder()
160 .setSdncRequestHeader(new SdncRequestHeaderBuilder(service.getSdncRequestHeader())
161 .setRpcAction(RpcActions.ServiceDelete)
163 .setServiceDeleteReqInfo(new ServiceDeleteReqInfoBuilder()
164 .setServiceName(serviceNameToReroute)
165 .setTailRetention(ServiceDeleteReqInfo.TailRetention.No)
169 String httpResponseCode = res.get().getResult().getConfigurationResponseCommon().getResponseCode();
170 if (httpResponseCode.equals(ResponseCodes.RESPONSE_OK)) {
171 mapServiceInputReroute.put(serviceNameToReroute, new ServiceInput(
172 new ServiceCreateInputBuilder()
173 .setServiceName(serviceNameToReroute)
174 .setCommonId(service.getCommonId())
175 .setConnectionType(service.getConnectionType())
176 .setServiceAEnd(new ServiceAEndBuilder(service.getServiceAEnd()).build())
177 .setServiceZEnd(new ServiceZEndBuilder(service.getServiceZEnd()).build())
178 .setHardConstraints(service.getHardConstraints())
179 .setSoftConstraints(service.getSoftConstraints())
180 .setSdncRequestHeader(service.getSdncRequestHeader())
181 .setCustomer(service.getCustomer())
182 .setCustomerContact(service.getCustomerContact())
183 .setServiceResiliency(service.getServiceResiliency())
184 .setDueDate(service.getDueDate())
185 .setOperatorContact(service.getOperatorContact())
187 LOG.info("ServiceRerouteStep1 (deletion of the service) in progress");
189 LOG.warn("ServiceRerouteStep1 (deletion of the service) failed '{}' http code ", httpResponseCode);
191 } catch (ExecutionException | InterruptedException e) {
192 LOG.warn("ServiceRerouteStep1 FAILED ! ", e);
197 * Second step of the reroute : apply a service-create RPC. This method is called after the first step of reroute
198 * when the service has been successfully deleted.
200 * @param serviceNameToReroute Name of the service
202 private void serviceRerouteStep2(String serviceNameToReroute) {
203 ListenableFuture<RpcResult<ServiceCreateOutput>> res = this.servicehandlerImpl.serviceCreate(
204 mapServiceInputReroute.get(serviceNameToReroute).getServiceCreateInput());
206 String httpResponseCode = res.get().getResult().getConfigurationResponseCommon().getResponseCode();
207 if (httpResponseCode.equals(ResponseCodes.RESPONSE_OK)) {
208 LOG.info("ServiceRerouteStep2 (creation of the new service) in progress");
210 LOG.warn("ServiceRerouteStep2 (creation of the new service) failed '{}' http code ", httpResponseCode);
212 } catch (ExecutionException | InterruptedException e) {
213 LOG.warn("ServiceRerouteStep2 FAILED ! ", e);
215 mapServiceInputReroute.remove(serviceNameToReroute);
219 * Send notification to NBI notification in order to publish message.
221 * @param service PublishNotificationAlarmService
223 private void sendNbiNotification(PublishNotificationAlarmService service) {
225 notificationPublishService.putNotification(service);
226 } catch (InterruptedException e) {
227 LOG.warn("Cannot send notification to nbi", e);
228 Thread.currentThread().interrupt();