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.service.ServiceDataStoreOperations;
27 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev230526.Restorable;
28 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev230526.RpcActions;
29 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev230526.routing.metric.RoutingMetric;
30 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev230526.sdnc.request.header.SdncRequestHeaderBuilder;
31 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev230526.service.resiliency.ServiceResiliency;
32 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.state.types.rev191129.State;
33 import org.opendaylight.yang.gen.v1.http.org.openroadm.equipment.states.types.rev191129.AdminStates;
34 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.OrgOpenroadmServiceService;
35 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.ServiceCreateInputBuilder;
36 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.ServiceCreateOutput;
37 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.ServiceDeleteInputBuilder;
38 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.ServiceDeleteOutput;
39 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.ServiceRerouteInput;
40 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.ServiceRerouteInputBuilder;
41 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.ServiceRerouteOutput;
42 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.service.create.input.ServiceAEndBuilder;
43 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.service.create.input.ServiceZEndBuilder;
44 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.service.delete.input.ServiceDeleteReqInfo;
45 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.service.delete.input.ServiceDeleteReqInfoBuilder;
46 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev230526.service.list.Services;
47 import org.opendaylight.yang.gen.v1.nbi.notifications.rev230726.PublishNotificationAlarmService;
48 import org.opendaylight.yang.gen.v1.nbi.notifications.rev230726.PublishNotificationAlarmServiceBuilder;
49 import org.opendaylight.yangtools.yang.common.RpcResult;
50 import org.osgi.service.component.annotations.Activate;
51 import org.osgi.service.component.annotations.Component;
52 import org.osgi.service.component.annotations.Reference;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
57 public class ServiceListener implements DataTreeChangeListener<Services> {
59 private static final Logger LOG = LoggerFactory.getLogger(ServiceListener.class);
60 private static final String PUBLISHER = "ServiceListener";
61 private OrgOpenroadmServiceService servicehandlerImpl;
62 private ServiceDataStoreOperations serviceDataStoreOperations;
63 private NotificationPublishService notificationPublishService;
64 private Map<String, ServiceInput> mapServiceInputReroute;
65 private final ScheduledExecutorService executor;
68 public ServiceListener(@Reference OrgOpenroadmServiceService servicehandlerImpl,
69 @Reference ServiceDataStoreOperations serviceDataStoreOperations,
70 @Reference NotificationPublishService notificationPublishService) {
71 this.servicehandlerImpl = servicehandlerImpl;
72 this.notificationPublishService = notificationPublishService;
73 this.serviceDataStoreOperations = serviceDataStoreOperations;
74 this.executor = MoreExecutors.getExitingScheduledExecutorService(new ScheduledThreadPoolExecutor(4));
75 mapServiceInputReroute = new HashMap<>();
79 public void onDataTreeChanged(Collection<DataTreeModification<Services>> changes) {
80 LOG.info("onDataTreeChanged - {}", this.getClass().getSimpleName());
81 for (DataTreeModification<Services> change : changes) {
82 DataObjectModification<Services> rootService = change.getRootNode();
83 if (rootService.getDataBefore() == null) {
86 String serviceInputName = rootService.getDataBefore().key().getServiceName();
87 switch (rootService.getModificationType()) {
89 LOG.info("Service {} correctly deleted from controller", serviceInputName);
90 if (mapServiceInputReroute.get(serviceInputName) != null) {
91 serviceRerouteStep2(serviceInputName);
95 Services inputBefore = rootService.getDataBefore();
96 Services inputAfter = rootService.getDataAfter();
97 if (inputBefore.getOperationalState() == State.InService
98 && inputAfter.getOperationalState() == State.OutOfService) {
99 LOG.info("Service {} is becoming outOfService", serviceInputName);
100 sendNbiNotification(new PublishNotificationAlarmServiceBuilder()
101 .setServiceName(inputAfter.getServiceName())
102 .setConnectionType(inputAfter.getConnectionType())
103 .setMessage("The service is now outOfService")
104 .setOperationalState(State.OutOfService)
105 .setPublisherName(PUBLISHER)
107 if (inputAfter.getAdministrativeState() == AdminStates.InService
108 && inputAfter.getServiceResiliency() != null
109 && inputAfter.getServiceResiliency().getResiliency() != null
110 && inputAfter.getServiceResiliency().getResiliency().equals(Restorable.VALUE)) {
111 LOG.info("Attempting to reroute the service '{}'...", serviceInputName);
112 if (!serviceRerouteCheck(serviceInputName, inputAfter.getServiceResiliency(),
113 inputAfter.getRoutingMetric())) {
114 LOG.info("No other path available, cancelling reroute process of service '{}'...",
118 mapServiceInputReroute.put(serviceInputName, null);
119 if (inputAfter.getServiceResiliency().getHoldoffTime() != null) {
120 LOG.info("Waiting hold off time before rerouting...");
123 if (mapServiceInputReroute.containsKey(serviceInputName)
124 && mapServiceInputReroute.get(serviceInputName) == null) {
125 serviceRerouteStep1(serviceInputName);
127 LOG.info("Cancelling reroute process of service '{}'...",
131 Long.parseLong(String.valueOf(inputAfter.getServiceResiliency()
133 TimeUnit.MILLISECONDS);
135 serviceRerouteStep1(serviceInputName);
138 } else if (inputAfter.getAdministrativeState() == AdminStates.InService
139 && inputBefore.getOperationalState() == State.OutOfService
140 && inputAfter.getOperationalState() == State.InService) {
141 LOG.info("Service {} is becoming InService", serviceInputName);
142 sendNbiNotification(new PublishNotificationAlarmServiceBuilder()
143 .setServiceName(inputAfter.getServiceName())
144 .setConnectionType(inputAfter.getConnectionType())
145 .setMessage("The service is now inService")
146 .setOperationalState(State.InService)
147 .setPublisherName(PUBLISHER)
149 if (mapServiceInputReroute.containsKey(serviceInputName)
150 && mapServiceInputReroute.get(serviceInputName) == null) {
151 mapServiceInputReroute.remove(serviceInputName);
156 LOG.debug("Unknown modification type {}", rootService.getModificationType().name());
163 * First step of the reroute : apply a service-delete RPC to the service.
165 * @param serviceNameToReroute Name of the service
167 private void serviceRerouteStep1(String serviceNameToReroute) {
168 mapServiceInputReroute.remove(serviceNameToReroute);
169 Optional<Services> serviceOpt = serviceDataStoreOperations.getService(serviceNameToReroute);
170 if (serviceOpt.isEmpty()) {
171 LOG.warn("Service '{}' does not exist in datastore", serviceNameToReroute);
174 Services service = serviceOpt.orElseThrow();
175 ListenableFuture<RpcResult<ServiceDeleteOutput>> res = this.servicehandlerImpl.serviceDelete(
176 new ServiceDeleteInputBuilder()
177 .setSdncRequestHeader(new SdncRequestHeaderBuilder(service.getSdncRequestHeader())
178 .setRpcAction(RpcActions.ServiceDelete)
180 .setServiceDeleteReqInfo(new ServiceDeleteReqInfoBuilder()
181 .setServiceName(serviceNameToReroute)
182 .setTailRetention(ServiceDeleteReqInfo.TailRetention.No)
186 String httpResponseCode = res.get().getResult().getConfigurationResponseCommon().getResponseCode();
187 if (httpResponseCode.equals(ResponseCodes.RESPONSE_OK)) {
188 mapServiceInputReroute.put(serviceNameToReroute, new ServiceInput(
189 new ServiceCreateInputBuilder()
190 .setServiceName(serviceNameToReroute)
191 .setCommonId(service.getCommonId())
192 .setConnectionType(service.getConnectionType())
193 .setServiceAEnd(new ServiceAEndBuilder(service.getServiceAEnd()).build())
194 .setServiceZEnd(new ServiceZEndBuilder(service.getServiceZEnd()).build())
195 .setHardConstraints(service.getHardConstraints())
196 .setSoftConstraints(service.getSoftConstraints())
197 .setSdncRequestHeader(service.getSdncRequestHeader())
198 .setCustomer(service.getCustomer())
199 .setCustomerContact(service.getCustomerContact())
200 .setServiceResiliency(service.getServiceResiliency())
201 .setDueDate(service.getDueDate())
202 .setOperatorContact(service.getOperatorContact())
204 LOG.info("ServiceRerouteStep1 (deletion of the service) in progress");
206 LOG.warn("ServiceRerouteStep1 (deletion of the service) failed '{}' http code ", httpResponseCode);
208 } catch (ExecutionException | InterruptedException e) {
209 LOG.warn("ServiceRerouteStep1 FAILED ! ", e);
214 * Second step of the reroute : apply a service-create RPC. This method is called after the first step of reroute
215 * when the service has been successfully deleted.
217 * @param serviceNameToReroute Name of the service
219 private void serviceRerouteStep2(String serviceNameToReroute) {
220 ListenableFuture<RpcResult<ServiceCreateOutput>> res = this.servicehandlerImpl.serviceCreate(
221 mapServiceInputReroute.get(serviceNameToReroute).getServiceCreateInput());
223 String httpResponseCode = res.get().getResult().getConfigurationResponseCommon().getResponseCode();
224 if (httpResponseCode.equals(ResponseCodes.RESPONSE_OK)) {
225 LOG.info("ServiceRerouteStep2 (creation of the new service) in progress");
227 LOG.warn("ServiceRerouteStep2 (creation of the new service) failed '{}' http code ", httpResponseCode);
229 } catch (ExecutionException | InterruptedException e) {
230 LOG.warn("ServiceRerouteStep2 FAILED ! ", e);
232 mapServiceInputReroute.remove(serviceNameToReroute);
236 * Prior to the reroute steps: check that an alternative route of the service is possible.
238 * @param serviceNameToReroute Name of the service
239 * @param serviceResiliency Resiliency of the service
240 * @param routingMetric Metric of the routing
242 private boolean serviceRerouteCheck(String serviceNameToReroute, ServiceResiliency serviceResiliency,
243 RoutingMetric routingMetric) {
244 ServiceRerouteInput serviceRerouteInput = new ServiceRerouteInputBuilder()
245 .setServiceName(serviceNameToReroute)
246 .setServiceResiliency(serviceResiliency)
247 .setRoutingMetric(routingMetric)
248 .setSdncRequestHeader(new SdncRequestHeaderBuilder()
249 .setRpcAction(RpcActions.ServiceReroute)
252 ListenableFuture<RpcResult<ServiceRerouteOutput>> res = this.servicehandlerImpl.serviceReroute(
253 serviceRerouteInput);
255 return res.get().getResult().getConfigurationResponseCommon().getResponseCode()
256 .equals(ResponseCodes.RESPONSE_OK);
257 } catch (ExecutionException | InterruptedException e) {
258 LOG.warn("ServiceRerouteCheck FAILED ! ", e);
264 * Send notification to NBI notification in order to publish message.
266 * @param service PublishNotificationAlarmService
268 private void sendNbiNotification(PublishNotificationAlarmService service) {
270 notificationPublishService.putNotification(service);
271 } catch (InterruptedException e) {
272 LOG.warn("Cannot send notification to nbi", e);
273 Thread.currentThread().interrupt();