0080c77c1e8e44b778cc2f074bfe32a87f147d8a
[transportpce.git] / tapi / src / main / java / org / opendaylight / transportpce / tapi / listeners / TapiRendererListenerImpl.java
1 /*
2  * Copyright © 2021 Nokia, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.transportpce.tapi.listeners;
9
10 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
11 import java.nio.charset.StandardCharsets;
12 import java.time.OffsetDateTime;
13 import java.time.ZoneOffset;
14 import java.time.format.DateTimeFormatter;
15 import java.util.HashMap;
16 import java.util.Map;
17 import java.util.Optional;
18 import java.util.UUID;
19 import java.util.concurrent.ExecutionException;
20 import org.opendaylight.mdsal.binding.api.DataBroker;
21 import org.opendaylight.mdsal.binding.api.NotificationPublishService;
22 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
23 import org.opendaylight.transportpce.common.network.NetworkTransactionImpl;
24 import org.opendaylight.transportpce.common.network.NetworkTransactionService;
25 import org.opendaylight.transportpce.common.network.RequestProcessor;
26 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev210915.RendererRpcResultSp;
27 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev210915.TransportpceRendererListener;
28 import org.opendaylight.yang.gen.v1.nbi.notifications.rev211013.PublishTapiNotificationService;
29 import org.opendaylight.yang.gen.v1.nbi.notifications.rev211013.PublishTapiNotificationServiceBuilder;
30 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev181210.AdministrativeState;
31 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev181210.Context;
32 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev181210.DateAndTime;
33 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev181210.LifecycleState;
34 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev181210.OperationalState;
35 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev181210.Uuid;
36 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev181210.global._class.Name;
37 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev181210.global._class.NameKey;
38 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev181210.Context1;
39 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.ConnectionBuilder;
40 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.ConnectionKey;
41 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.ConnectivityService;
42 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.ConnectivityServiceBuilder;
43 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.ConnectivityServiceKey;
44 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev181210.connectivity.service.Connection;
45 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.notification.rev181210.NotificationType;
46 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.notification.rev181210.ObjectType;
47 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.notification.rev181210.notification.ChangedAttributes;
48 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.notification.rev181210.notification.ChangedAttributesBuilder;
49 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.notification.rev181210.notification.ChangedAttributesKey;
50 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.notification.rev181210.notification.TargetObjectName;
51 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.notification.rev181210.notification.TargetObjectNameBuilder;
52 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.notification.rev181210.notification.TargetObjectNameKey;
53 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57 public class TapiRendererListenerImpl implements TransportpceRendererListener {
58
59     private static final Logger LOG = LoggerFactory.getLogger(TapiRendererListenerImpl.class);
60     private final DataBroker dataBroker;
61     private Uuid serviceUuid;
62     private RendererRpcResultSp serviceRpcResultSp;
63     private final NetworkTransactionService networkTransactionService;
64     private final NotificationPublishService notificationPublishService;
65
66     public TapiRendererListenerImpl(DataBroker dataBroker, NotificationPublishService notificationPublishService) {
67         this.dataBroker = dataBroker;
68         this.networkTransactionService = new NetworkTransactionImpl(new RequestProcessor(this.dataBroker));
69         this.notificationPublishService = notificationPublishService;
70     }
71
72     @Override
73     public void onRendererRpcResultSp(RendererRpcResultSp notification) {
74         if (compareServiceRpcResultSp(notification)) {
75             LOG.warn("ServiceRpcResultSp already wired !");
76             return;
77         }
78         serviceRpcResultSp = notification;
79         int notifType = serviceRpcResultSp.getNotificationType().getIntValue();
80         LOG.info("Renderer '{}' Notification received : {}", serviceRpcResultSp.getNotificationType().getName(),
81                 notification);
82         /* service-implementation-request. */
83         if (notifType == 3) {
84             onServiceImplementationResult(notification);
85         }
86     }
87
88     /**
89      * Process service implementation result for serviceName.
90      * @param notification RendererRpcResultSp
91      */
92     private void onServiceImplementationResult(RendererRpcResultSp notification) {
93         switch (serviceRpcResultSp.getStatus()) {
94             case Successful:
95                 if (this.serviceUuid != null) {
96                     onSuccededServiceImplementation();
97                 }
98                 break;
99             case Failed:
100                 onFailedServiceImplementation(notification.getServiceName());
101                 break;
102             case  Pending:
103                 LOG.warn("Service Implementation still pending according to RpcStatusEx");
104                 break;
105             default:
106                 LOG.warn("Service Implementation has an unknown RpcStatusEx code");
107                 break;
108         }
109     }
110
111     /**
112      * Process succeeded service implementation for service.
113      */
114     private void onSuccededServiceImplementation() {
115         LOG.info("Service implemented !");
116         // TODO: update Connections and Connectivity Service states
117         ConnectivityService connectivityService = getConnectivityService(this.serviceUuid);
118         if (connectivityService == null) {
119             LOG.error("Couldnt retrieve service from datastore");
120             return;
121         }
122         LOG.info("Connectivity service = {}", connectivityService);
123         // TODO --> this throws error because the renderer goes really fast. Is this normal??
124         ConnectivityService updtConnServ = new ConnectivityServiceBuilder(connectivityService)
125             .setAdministrativeState(AdministrativeState.UNLOCKED)
126             .setLifecycleState(LifecycleState.INSTALLED)
127             .setOperationalState(OperationalState.ENABLED)
128             .build();
129         for (Connection connection:updtConnServ.nonnullConnection().values()) {
130             updateConnectionState(connection.getConnectionUuid());
131         }
132         updateConnectivityService(updtConnServ);
133         // TODO: need to send notification to kafka in case the topic exists!!
134         sendNbiNotification(createNbiNotification(updtConnServ));
135     }
136
137     /**
138      * Process failed service implementation for serviceName.
139      * @param serviceName String
140      */
141     private void onFailedServiceImplementation(String serviceName) {
142         LOG.error("Renderer implementation failed !");
143         LOG.info("PCE cancel resource done OK !");
144         Uuid suuid = new Uuid(UUID.nameUUIDFromBytes(serviceName.getBytes(StandardCharsets.UTF_8))
145                 .toString());
146         // get connections of connectivity service and remove them from tapi context and then remove
147         //  service from context. The CEPs are maintained as they could be reused by another service
148         ConnectivityService connService = getConnectivityService(suuid);
149         if (connService == null) {
150             LOG.error("Service doesnt exist in tapi context");
151             return;
152         }
153         for (Connection connection:connService.getConnection().values()) {
154             deleteConnection(connection.getConnectionUuid());
155         }
156         deleteConnectivityService(suuid);
157     }
158
159     @SuppressFBWarnings(
160             value = "ES_COMPARING_STRINGS_WITH_EQ",
161             justification = "false positives, not strings but real object references comparisons")
162     private Boolean compareServiceRpcResultSp(RendererRpcResultSp notification) {
163         if (serviceRpcResultSp == null) {
164             return false;
165         }
166         if (serviceRpcResultSp.getNotificationType() != notification.getNotificationType()) {
167             return false;
168         }
169         if (serviceRpcResultSp.getServiceName() != notification.getServiceName()) {
170             return false;
171         }
172         if (serviceRpcResultSp.getStatus() != notification.getStatus()) {
173             return false;
174         }
175         if (serviceRpcResultSp.getStatusMessage() != notification.getStatusMessage()) {
176             return false;
177         }
178         return true;
179     }
180
181     private ConnectivityService getConnectivityService(Uuid suuid) {
182         // TODO: verify this is correct. Should we identify the context IID with the context UUID??
183         try {
184             // First read connectivity service with service uuid and update info
185             InstanceIdentifier<ConnectivityService> connectivityServIID =
186                 InstanceIdentifier.builder(Context.class).augmentation(Context1.class)
187                     .child(org.opendaylight.yang.gen.v1.urn
188                         .onf.otcc.yang.tapi.connectivity.rev181210.context.ConnectivityContext.class)
189                     .child(ConnectivityService.class, new ConnectivityServiceKey(suuid))
190                     .build();
191
192             Optional<ConnectivityService> optConnServ =
193                 this.networkTransactionService.read(LogicalDatastoreType.OPERATIONAL, connectivityServIID).get();
194             if (!optConnServ.isPresent()) {
195                 LOG.error("Connectivity service not found in tapi context");
196                 return null;
197             }
198             return optConnServ.get();
199         } catch (InterruptedException | ExecutionException e) {
200             LOG.error("Failed to merge TAPI connectivity", e);
201             return null;
202         }
203     }
204
205     private void updateConnectionState(Uuid connectionUuid) {
206         // TODO: verify this is correct. Should we identify the context IID with the context UUID??
207         try {
208             // First read connection with connection uuid and update info
209             InstanceIdentifier<org.opendaylight.yang.gen.v1.urn
210                 .onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.Connection> connectionIID =
211                 InstanceIdentifier.builder(Context.class).augmentation(Context1.class)
212                     .child(org.opendaylight.yang.gen.v1.urn
213                         .onf.otcc.yang.tapi.connectivity.rev181210.context.ConnectivityContext.class)
214                     .child(org.opendaylight.yang.gen.v1.urn
215                             .onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.Connection.class,
216                         new ConnectionKey(connectionUuid))
217                     .build();
218
219             Optional<org.opendaylight.yang.gen.v1.urn
220                 .onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.Connection> optConn =
221                 this.networkTransactionService.read(LogicalDatastoreType.OPERATIONAL, connectionIID).get();
222             if (!optConn.isPresent()) {
223                 LOG.error("Connection not found in tapi context");
224                 return;
225             }
226             org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.Connection
227                 newConnection = new ConnectionBuilder(optConn.get()).setLifecycleState(LifecycleState.INSTALLED)
228                     .setOperationalState(OperationalState.ENABLED).build();
229             // merge in datastore
230             this.networkTransactionService.merge(LogicalDatastoreType.OPERATIONAL, connectionIID,
231                     newConnection);
232             this.networkTransactionService.commit().get();
233             LOG.info("TAPI connection merged successfully.");
234         } catch (InterruptedException | ExecutionException e) {
235             LOG.error("Failed to merge TAPI connection", e);
236         }
237     }
238
239     private void updateConnectivityService(ConnectivityService updtConnServ) {
240         // TODO: verify this is correct. Should we identify the context IID with the context UUID??
241         try {
242             // First read connectivity service with connectivity service uuid and update info
243             InstanceIdentifier<ConnectivityService> connServIID =
244                 InstanceIdentifier.builder(Context.class).augmentation(Context1.class)
245                     .child(org.opendaylight.yang.gen.v1.urn
246                         .onf.otcc.yang.tapi.connectivity.rev181210.context.ConnectivityContext.class)
247                     .child(ConnectivityService.class, new ConnectivityServiceKey(updtConnServ.getUuid()))
248                     .build();
249
250             Optional<ConnectivityService> optConnServ =
251                 this.networkTransactionService.read(LogicalDatastoreType.OPERATIONAL, connServIID).get();
252             if (!optConnServ.isPresent()) {
253                 LOG.error("Connection not found in tapi context");
254                 return;
255             }
256             ConnectivityService newConnServ = new ConnectivityServiceBuilder(updtConnServ).build();
257             // merge in datastore
258             this.networkTransactionService.merge(LogicalDatastoreType.OPERATIONAL, connServIID,
259                     newConnServ);
260             this.networkTransactionService.commit().get();
261             LOG.info("TAPI connectivity service merged successfully.");
262         } catch (InterruptedException | ExecutionException e) {
263             LOG.error("Failed to merge TAPI connectivity service", e);
264         }
265     }
266
267     private void deleteConnectivityService(Uuid suuid) {
268         // First read connectivity service with service uuid and update info
269         InstanceIdentifier<ConnectivityService> connectivityServIID =
270             InstanceIdentifier.builder(Context.class).augmentation(Context1.class)
271                 .child(org.opendaylight.yang.gen.v1.urn
272                     .onf.otcc.yang.tapi.connectivity.rev181210.context.ConnectivityContext.class)
273                 .child(ConnectivityService.class, new ConnectivityServiceKey(suuid))
274                 .build();
275         try {
276             this.networkTransactionService.delete(LogicalDatastoreType.OPERATIONAL, connectivityServIID);
277             this.networkTransactionService.commit().get();
278         } catch (InterruptedException | ExecutionException e) {
279             LOG.error("Failed to delete TAPI connectivity service", e);
280         }
281     }
282
283     private void deleteConnection(Uuid connectionUuid) {
284         // First read connectivity service with service uuid and update info
285         InstanceIdentifier<org.opendaylight.yang.gen.v1
286             .urn.onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.Connection> connectionIID =
287             InstanceIdentifier.builder(Context.class).augmentation(Context1.class)
288                 .child(org.opendaylight.yang.gen.v1.urn
289                     .onf.otcc.yang.tapi.connectivity.rev181210.context.ConnectivityContext.class)
290                 .child(org.opendaylight.yang.gen.v1.urn
291                         .onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.Connection.class,
292                     new org.opendaylight.yang.gen.v1.urn
293                         .onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.ConnectionKey(
294                             connectionUuid))
295                 .build();
296         try {
297             this.networkTransactionService.delete(LogicalDatastoreType.OPERATIONAL, connectionIID);
298             this.networkTransactionService.commit().get();
299         } catch (InterruptedException | ExecutionException e) {
300             LOG.error("Failed to delete TAPI connection", e);
301         }
302     }
303
304     private void sendNbiNotification(PublishTapiNotificationService service) {
305         try {
306             this.notificationPublishService.putNotification(service);
307         } catch (InterruptedException e) {
308             LOG.warn("Cannot send notification to nbi", e);
309             Thread.currentThread().interrupt();
310         }
311     }
312
313     private PublishTapiNotificationService createNbiNotification(ConnectivityService connService) {
314         if (connService == null) {
315             LOG.error("ConnService is null");
316             return null;
317         }
318         /*
319         Map<ChangedAttributesKey, ChangedAttributes> changedStates = changedAttributesMap.entrySet()
320                 .stream()
321                 .filter(e -> e.getKey().getValueName().equals("administrative")
322                         || e.getKey().getValueName().equals("operational"))
323                 .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()));
324
325          */
326         Map<ChangedAttributesKey, ChangedAttributes> changedStates = new HashMap<>();
327         changedStates.put(new ChangedAttributesKey("administrativeState"),
328             new ChangedAttributesBuilder()
329                 .setNewValue(connService.getAdministrativeState().getName())
330                 .setOldValue(AdministrativeState.LOCKED.getName())
331                 .setValueName("administrativeState").build());
332         changedStates.put(new ChangedAttributesKey("operationalState"),
333             new ChangedAttributesBuilder()
334                 .setNewValue(connService.getOperationalState().getName())
335                 .setOldValue(OperationalState.DISABLED.getName())
336                 .setValueName("operationalState").build());
337         DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssxxx");
338         OffsetDateTime offsetDateTime = OffsetDateTime.now(ZoneOffset.UTC);
339         DateAndTime datetime = new DateAndTime(dtf.format(offsetDateTime));
340         Map<TargetObjectNameKey, TargetObjectName> targetObjectNames = new HashMap<>();
341         if (connService.getName() != null) {
342             for (Map.Entry<NameKey, Name> entry : connService.getName().entrySet()) {
343                 targetObjectNames.put(new TargetObjectNameKey(entry.getKey().getValueName()),
344                     new TargetObjectNameBuilder()
345                         .setValueName(entry.getValue().getValueName())
346                         .setValue(entry.getValue().getValue())
347                         .build());
348             }
349         }
350
351         return new PublishTapiNotificationServiceBuilder()
352             .setUuid(new Uuid(UUID.randomUUID().toString()))
353             .setTopic(connService.getUuid().getValue())
354             .setTargetObjectIdentifier(connService.getUuid())
355             .setNotificationType(NotificationType.ATTRIBUTEVALUECHANGE)
356             .setChangedAttributes(changedStates)
357             .setEventTimeStamp(datetime)
358             .setTargetObjectName(targetObjectNames)
359             .setTargetObjectType(ObjectType.CONNECTIVITYSERVICE)
360             .setLayerProtocolName(connService.getServiceLayer())
361             .build();
362     }
363
364     public void setServiceUuid(Uuid serviceUuid) {
365         this.serviceUuid = serviceUuid;
366     }
367 }