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 static org.opendaylight.transportpce.servicehandler.ModelMappingUtils.createServiceAEndReroute;
11 import static org.opendaylight.transportpce.servicehandler.ModelMappingUtils.createServiceZEndReroute;
13 import com.google.common.util.concurrent.ListenableFuture;
14 import com.google.common.util.concurrent.MoreExecutors;
15 import java.util.Collection;
16 import java.util.HashMap;
18 import java.util.Optional;
19 import java.util.concurrent.ExecutionException;
20 import java.util.concurrent.ScheduledExecutorService;
21 import java.util.concurrent.ScheduledThreadPoolExecutor;
22 import java.util.concurrent.TimeUnit;
23 import org.opendaylight.mdsal.binding.api.DataObjectModification;
24 import org.opendaylight.mdsal.binding.api.DataTreeChangeListener;
25 import org.opendaylight.mdsal.binding.api.DataTreeModification;
26 import org.opendaylight.mdsal.binding.api.NotificationPublishService;
27 import org.opendaylight.transportpce.common.ResponseCodes;
28 import org.opendaylight.transportpce.pce.service.PathComputationService;
29 import org.opendaylight.transportpce.servicehandler.ServiceInput;
30 import org.opendaylight.transportpce.servicehandler.impl.ServicehandlerImpl;
31 import org.opendaylight.transportpce.servicehandler.service.ServiceDataStoreOperations;
32 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.PathComputationRerouteRequestInput;
33 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.PathComputationRerouteRequestInputBuilder;
34 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.PathComputationRerouteRequestOutput;
35 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.path.computation.reroute.request.input.EndpointsBuilder;
36 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev211210.Restorable;
37 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev211210.RpcActions;
38 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.service.types.rev211210.sdnc.request.header.SdncRequestHeaderBuilder;
39 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.state.types.rev191129.State;
40 import org.opendaylight.yang.gen.v1.http.org.openroadm.equipment.states.types.rev191129.AdminStates;
41 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.ServiceCreateInputBuilder;
42 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.ServiceCreateOutput;
43 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.ServiceDeleteInputBuilder;
44 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.ServiceDeleteOutput;
45 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.service.create.input.ServiceAEndBuilder;
46 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.service.create.input.ServiceZEndBuilder;
47 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.service.delete.input.ServiceDeleteReqInfo;
48 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.service.delete.input.ServiceDeleteReqInfoBuilder;
49 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.service.list.Services;
50 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev210705.path.description.atoz.direction.AToZ;
51 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev210705.path.description.atoz.direction.AToZKey;
52 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.pathdescription.rev210705.pce.resource.resource.resource.TerminationPoint;
53 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev220118.PceMetric;
54 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.servicepath.rev171017.service.path.list.ServicePaths;
55 import org.opendaylight.yang.gen.v1.nbi.notifications.rev211013.PublishNotificationAlarmService;
56 import org.opendaylight.yang.gen.v1.nbi.notifications.rev211013.PublishNotificationAlarmServiceBuilder;
57 import org.opendaylight.yangtools.yang.common.RpcResult;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
61 public class ServiceListener implements DataTreeChangeListener<Services> {
63 private static final Logger LOG = LoggerFactory.getLogger(ServiceListener.class);
64 private static final String PUBLISHER = "ServiceListener";
65 private ServicehandlerImpl servicehandlerImpl;
66 private ServiceDataStoreOperations serviceDataStoreOperations;
67 private NotificationPublishService notificationPublishService;
68 private PathComputationService pathComputationService;
69 private Map<String, ServiceInput> mapServiceInputReroute;
70 private final ScheduledExecutorService executor;
72 public ServiceListener(ServicehandlerImpl servicehandlerImpl, ServiceDataStoreOperations serviceDataStoreOperations,
73 NotificationPublishService notificationPublishService,
74 PathComputationService pathComputationService) {
75 this.servicehandlerImpl = servicehandlerImpl;
76 this.notificationPublishService = notificationPublishService;
77 this.serviceDataStoreOperations = serviceDataStoreOperations;
78 this.pathComputationService = pathComputationService;
79 this.executor = MoreExecutors.getExitingScheduledExecutorService(new ScheduledThreadPoolExecutor(4));
80 mapServiceInputReroute = new HashMap<>();
84 public void onDataTreeChanged(Collection<DataTreeModification<Services>> changes) {
85 LOG.info("onDataTreeChanged - {}", this.getClass().getSimpleName());
86 for (DataTreeModification<Services> change : changes) {
87 DataObjectModification<Services> rootService = change.getRootNode();
88 if (rootService.getDataBefore() == null) {
91 String serviceInputName = rootService.getDataBefore().key().getServiceName();
92 switch (rootService.getModificationType()) {
94 LOG.info("Service {} correctly deleted from controller", serviceInputName);
95 if (mapServiceInputReroute.get(serviceInputName) != null) {
96 serviceRerouteStep2(serviceInputName);
100 Services inputBefore = rootService.getDataBefore();
101 Services inputAfter = rootService.getDataAfter();
102 if (inputBefore.getOperationalState() == State.InService
103 && inputAfter.getOperationalState() == State.OutOfService) {
104 LOG.info("Service {} is becoming outOfService", serviceInputName);
105 sendNbiNotification(new PublishNotificationAlarmServiceBuilder()
106 .setServiceName(inputAfter.getServiceName())
107 .setConnectionType(inputAfter.getConnectionType())
108 .setMessage("The service is now outOfService")
109 .setOperationalState(State.OutOfService)
110 .setPublisherName(PUBLISHER)
112 if (inputAfter.getAdministrativeState() == AdminStates.InService
113 && inputAfter.getServiceResiliency() != null
114 && inputAfter.getServiceResiliency().getResiliency() != null
115 && inputAfter.getServiceResiliency().getResiliency().equals(Restorable.VALUE)) {
116 LOG.info("Attempting to reroute the service '{}'...", serviceInputName);
117 if (!serviceRerouteCheck(inputBefore)) {
118 LOG.info("No other path available, cancelling reroute process of service '{}'...",
122 mapServiceInputReroute.put(serviceInputName, null);
123 if (inputAfter.getServiceResiliency().getHoldoffTime() != null) {
124 LOG.info("Waiting hold off time before rerouting...");
127 if (mapServiceInputReroute.containsKey(serviceInputName)
128 && mapServiceInputReroute.get(serviceInputName) == null) {
129 serviceRerouteStep1(serviceInputName);
131 LOG.info("Cancelling reroute process of service '{}'...",
135 Long.parseLong(String.valueOf(inputAfter.getServiceResiliency()
137 TimeUnit.MILLISECONDS);
139 serviceRerouteStep1(serviceInputName);
142 } else if (inputAfter.getAdministrativeState() == AdminStates.InService
143 && inputBefore.getOperationalState() == State.OutOfService
144 && inputAfter.getOperationalState() == State.InService) {
145 LOG.info("Service {} is becoming InService", serviceInputName);
146 sendNbiNotification(new PublishNotificationAlarmServiceBuilder()
147 .setServiceName(inputAfter.getServiceName())
148 .setConnectionType(inputAfter.getConnectionType())
149 .setMessage("The service is now inService")
150 .setOperationalState(State.InService)
151 .setPublisherName(PUBLISHER)
153 if (mapServiceInputReroute.containsKey(serviceInputName)
154 && mapServiceInputReroute.get(serviceInputName) == null) {
155 mapServiceInputReroute.remove(serviceInputName);
160 LOG.debug("Unknown modification type {}", rootService.getModificationType().name());
167 * First step of the reroute : apply a service-delete RPC to the service.
169 * @param serviceNameToReroute Name of the service
171 private void serviceRerouteStep1(String serviceNameToReroute) {
172 mapServiceInputReroute.remove(serviceNameToReroute);
173 Optional<Services> serviceOpt = serviceDataStoreOperations.getService(serviceNameToReroute);
174 if (serviceOpt.isEmpty()) {
175 LOG.warn("Service '{}' does not exist in datastore", serviceNameToReroute);
178 Services service = serviceOpt.get();
179 ListenableFuture<RpcResult<ServiceDeleteOutput>> res = this.servicehandlerImpl.serviceDelete(
180 new ServiceDeleteInputBuilder()
181 .setSdncRequestHeader(new SdncRequestHeaderBuilder(service.getSdncRequestHeader())
182 .setRpcAction(RpcActions.ServiceDelete)
184 .setServiceDeleteReqInfo(new ServiceDeleteReqInfoBuilder()
185 .setServiceName(serviceNameToReroute)
186 .setTailRetention(ServiceDeleteReqInfo.TailRetention.No)
190 String httpResponseCode = res.get().getResult().getConfigurationResponseCommon().getResponseCode();
191 if (httpResponseCode.equals(ResponseCodes.RESPONSE_OK)) {
192 mapServiceInputReroute.put(serviceNameToReroute, new ServiceInput(
193 new ServiceCreateInputBuilder()
194 .setServiceName(serviceNameToReroute)
195 .setCommonId(service.getCommonId())
196 .setConnectionType(service.getConnectionType())
197 .setServiceAEnd(new ServiceAEndBuilder(service.getServiceAEnd()).build())
198 .setServiceZEnd(new ServiceZEndBuilder(service.getServiceZEnd()).build())
199 .setHardConstraints(service.getHardConstraints())
200 .setSoftConstraints(service.getSoftConstraints())
201 .setSdncRequestHeader(service.getSdncRequestHeader())
202 .setCustomer(service.getCustomer())
203 .setCustomerContact(service.getCustomerContact())
204 .setServiceResiliency(service.getServiceResiliency())
205 .setDueDate(service.getDueDate())
206 .setOperatorContact(service.getOperatorContact())
208 LOG.info("ServiceRerouteStep1 (deletion of the service) in progress");
210 LOG.warn("ServiceRerouteStep1 (deletion of the service) failed '{}' http code ", httpResponseCode);
212 } catch (ExecutionException | InterruptedException e) {
213 LOG.warn("ServiceRerouteStep1 FAILED ! ", e);
218 * Second step of the reroute : apply a service-create RPC. This method is called after the first step of reroute
219 * when the service has been successfully deleted.
221 * @param serviceNameToReroute Name of the service
223 private void serviceRerouteStep2(String serviceNameToReroute) {
224 ListenableFuture<RpcResult<ServiceCreateOutput>> res = this.servicehandlerImpl.serviceCreate(
225 mapServiceInputReroute.get(serviceNameToReroute).getServiceCreateInput());
227 String httpResponseCode = res.get().getResult().getConfigurationResponseCommon().getResponseCode();
228 if (httpResponseCode.equals(ResponseCodes.RESPONSE_OK)) {
229 LOG.info("ServiceRerouteStep2 (creation of the new service) in progress");
231 LOG.warn("ServiceRerouteStep2 (creation of the new service) failed '{}' http code ", httpResponseCode);
233 } catch (ExecutionException | InterruptedException e) {
234 LOG.warn("ServiceRerouteStep2 FAILED ! ", e);
236 mapServiceInputReroute.remove(serviceNameToReroute);
240 * Call the PCE RPC path-computation-reroute-request to check if any other path exists.
242 * @param input Service to be rerouted
244 protected boolean serviceRerouteCheck(Services input) {
245 Optional<ServicePaths> servicePaths = serviceDataStoreOperations.getServicePath(input.getServiceName());
246 if (servicePaths.isEmpty()) {
247 LOG.warn("Service path of '{}' does not exist in datastore", input.getServiceName());
250 // Get the network xpdr termination points
251 Map<AToZKey, AToZ> mapaToz = servicePaths.get().getPathDescription().getAToZDirection().getAToZ();
252 String aendtp = ((TerminationPoint) mapaToz.get(new AToZKey(String.valueOf(mapaToz.size() - 3)))
253 .getResource().getResource()).getTpId();
254 String zendtp = ((TerminationPoint) mapaToz.get(new AToZKey("2")).getResource()
255 .getResource()).getTpId();
256 PathComputationRerouteRequestInput inputPC = new PathComputationRerouteRequestInputBuilder()
257 .setHardConstraints(input.getHardConstraints())
258 .setSoftConstraints(input.getSoftConstraints())
259 .setServiceAEnd(createServiceAEndReroute(input.getServiceAEnd()))
260 .setServiceZEnd(createServiceZEndReroute(input.getServiceZEnd()))
261 .setPceRoutingMetric(PceMetric.TEMetric)
262 .setEndpoints(new EndpointsBuilder()
267 ListenableFuture<PathComputationRerouteRequestOutput> res =
268 pathComputationService.pathComputationRerouteRequest(inputPC);
270 return res.get().getConfigurationResponseCommon().getResponseCode().equals(ResponseCodes.RESPONSE_OK);
271 } catch (ExecutionException | InterruptedException e) {
272 LOG.warn("ServiceRerouteCheck FAILED ! ", e);
278 * Send notification to NBI notification in order to publish message.
280 * @param service PublishNotificationAlarmService
282 private void sendNbiNotification(PublishNotificationAlarmService service) {
284 notificationPublishService.putNotification(service);
285 } catch (InterruptedException e) {
286 LOG.warn("Cannot send notification to nbi", e);
287 Thread.currentThread().interrupt();