Fix few NPE in TAPI implementation
[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.Charset;
12 import java.util.Optional;
13 import java.util.UUID;
14 import java.util.concurrent.ExecutionException;
15 import org.opendaylight.mdsal.binding.api.DataBroker;
16 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
17 import org.opendaylight.transportpce.common.network.NetworkTransactionImpl;
18 import org.opendaylight.transportpce.common.network.NetworkTransactionService;
19 import org.opendaylight.transportpce.common.network.RequestProcessor;
20 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev210915.RendererRpcResultSp;
21 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev210915.TransportpceRendererListener;
22 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev181210.AdministrativeState;
23 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev181210.Context;
24 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev181210.LifecycleState;
25 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev181210.OperationalState;
26 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev181210.Uuid;
27 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev181210.Context1;
28 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.ConnectionBuilder;
29 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.ConnectionKey;
30 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.ConnectivityService;
31 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.ConnectivityServiceBuilder;
32 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.ConnectivityServiceKey;
33 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev181210.connectivity.service.Connection;
34 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 public class TapiRendererListenerImpl implements TransportpceRendererListener {
39
40     private static final Logger LOG = LoggerFactory.getLogger(TapiRendererListenerImpl.class);
41     private final DataBroker dataBroker;
42     private Uuid serviceUuid;
43     private RendererRpcResultSp serviceRpcResultSp;
44     private final NetworkTransactionService networkTransactionService;
45
46     public TapiRendererListenerImpl(DataBroker dataBroker) {
47         this.dataBroker = dataBroker;
48         this.networkTransactionService = new NetworkTransactionImpl(new RequestProcessor(this.dataBroker));
49     }
50
51     @Override
52     public void onRendererRpcResultSp(RendererRpcResultSp notification) {
53         if (compareServiceRpcResultSp(notification)) {
54             LOG.warn("ServiceRpcResultSp already wired !");
55             return;
56         }
57         serviceRpcResultSp = notification;
58         int notifType = serviceRpcResultSp.getNotificationType().getIntValue();
59         LOG.info("Renderer '{}' Notification received : {}", serviceRpcResultSp.getNotificationType().getName(),
60                 notification);
61         /* service-implementation-request. */
62         if (notifType == 3) {
63             onServiceImplementationResult(notification);
64         }
65     }
66
67     /**
68      * Process service implementation result for serviceName.
69      * @param notification RendererRpcResultSp
70      */
71     private void onServiceImplementationResult(RendererRpcResultSp notification) {
72         switch (serviceRpcResultSp.getStatus()) {
73             case Successful:
74                 if (this.serviceUuid != null) {
75                     onSuccededServiceImplementation();
76                 }
77                 break;
78             case Failed:
79                 onFailedServiceImplementation(notification.getServiceName());
80                 break;
81             case  Pending:
82                 LOG.warn("Service Implementation still pending according to RpcStatusEx");
83                 break;
84             default:
85                 LOG.warn("Service Implementation has an unknown RpcStatusEx code");
86                 break;
87         }
88     }
89
90     /**
91      * Process succeeded service implementation for service.
92      */
93     private void onSuccededServiceImplementation() {
94         LOG.info("Service implemented !");
95         // TODO: update Connections and Connectivity Service states
96         ConnectivityService connectivityService = getConnectivityService(this.serviceUuid);
97         if (connectivityService == null) {
98             LOG.error("Couldnt retrieve service from datastore");
99             return;
100         }
101         LOG.info("Connectivity service = {}", connectivityService.toString());
102         // TODO --> this throws error because the renderer goes really fast. Is this normal??
103         ConnectivityService updtConnServ = new ConnectivityServiceBuilder(connectivityService)
104             .setAdministrativeState(AdministrativeState.UNLOCKED)
105             .setLifecycleState(LifecycleState.INSTALLED)
106             .setOperationalState(OperationalState.ENABLED)
107             .build();
108         for (Connection connection:updtConnServ.nonnullConnection().values()) {
109             updateConnectionState(connection.getConnectionUuid());
110         }
111         updateConnectivityService(updtConnServ);
112     }
113
114     /**
115      * Process failed service implementation for serviceName.
116      * @param serviceName String
117      */
118     private void onFailedServiceImplementation(String serviceName) {
119         LOG.error("Renderer implementation failed !");
120         LOG.info("PCE cancel resource done OK !");
121         Uuid suuid = new Uuid(UUID.nameUUIDFromBytes(serviceName.getBytes(Charset.forName("UTF-8")))
122                 .toString());
123         // get connections of connectivity service and remove them from tapi context and then remove
124         //  service from context. The CEPs are maintained as they could be reused by another service
125         ConnectivityService connService = getConnectivityService(suuid);
126         if (connService == null) {
127             LOG.error("Service doesnt exist in tapi context");
128             return;
129         }
130         for (Connection connection:connService.getConnection().values()) {
131             deleteConnection(connection.getConnectionUuid());
132         }
133         deleteConnectivityService(suuid);
134     }
135
136     @SuppressFBWarnings(
137             value = "ES_COMPARING_STRINGS_WITH_EQ",
138             justification = "false positives, not strings but real object references comparisons")
139     private Boolean compareServiceRpcResultSp(RendererRpcResultSp notification) {
140         if (serviceRpcResultSp == null) {
141             return false;
142         }
143         if (serviceRpcResultSp.getNotificationType() != notification.getNotificationType()) {
144             return false;
145         }
146         if (serviceRpcResultSp.getServiceName() != notification.getServiceName()) {
147             return false;
148         }
149         if (serviceRpcResultSp.getStatus() != notification.getStatus()) {
150             return false;
151         }
152         if (serviceRpcResultSp.getStatusMessage() != notification.getStatusMessage()) {
153             return false;
154         }
155         return true;
156     }
157
158     private ConnectivityService getConnectivityService(Uuid suuid) {
159         // TODO: verify this is correct. Should we identify the context IID with the context UUID??
160         try {
161             // First read connectivity service with service uuid and update info
162             InstanceIdentifier<ConnectivityService> connectivityServIID =
163                 InstanceIdentifier.builder(Context.class).augmentation(Context1.class)
164                     .child(org.opendaylight.yang.gen.v1.urn
165                         .onf.otcc.yang.tapi.connectivity.rev181210.context.ConnectivityContext.class)
166                     .child(ConnectivityService.class, new ConnectivityServiceKey(suuid))
167                     .build();
168
169             Optional<ConnectivityService> optConnServ =
170                 this.networkTransactionService.read(LogicalDatastoreType.OPERATIONAL, connectivityServIID).get();
171             if (!optConnServ.isPresent()) {
172                 LOG.error("Connectivity service not found in tapi context");
173                 return null;
174             }
175             return optConnServ.get();
176         } catch (InterruptedException | ExecutionException e) {
177             LOG.error("Failed to merge TAPI connectivity", e);
178             return null;
179         }
180     }
181
182     private void updateConnectionState(Uuid connectionUuid) {
183         // TODO: verify this is correct. Should we identify the context IID with the context UUID??
184         try {
185             // First read connection with connection uuid and update info
186             InstanceIdentifier<org.opendaylight.yang.gen.v1.urn
187                 .onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.Connection> connectionIID =
188                 InstanceIdentifier.builder(Context.class).augmentation(Context1.class)
189                     .child(org.opendaylight.yang.gen.v1.urn
190                         .onf.otcc.yang.tapi.connectivity.rev181210.context.ConnectivityContext.class)
191                     .child(org.opendaylight.yang.gen.v1.urn
192                             .onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.Connection.class,
193                         new ConnectionKey(connectionUuid))
194                     .build();
195
196             Optional<org.opendaylight.yang.gen.v1.urn
197                 .onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.Connection> optConn =
198                 this.networkTransactionService.read(LogicalDatastoreType.OPERATIONAL, connectionIID).get();
199             if (!optConn.isPresent()) {
200                 LOG.error("Connection not found in tapi context");
201                 return;
202             }
203             org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.Connection
204                 newConnection = new ConnectionBuilder(optConn.get()).setLifecycleState(LifecycleState.INSTALLED)
205                     .setOperationalState(OperationalState.ENABLED).build();
206             // merge in datastore
207             this.networkTransactionService.merge(LogicalDatastoreType.OPERATIONAL, connectionIID,
208                     newConnection);
209             this.networkTransactionService.commit().get();
210             LOG.info("TAPI connection merged successfully.");
211         } catch (InterruptedException | ExecutionException e) {
212             LOG.error("Failed to merge TAPI connection", e);
213         }
214     }
215
216     private void updateConnectivityService(ConnectivityService updtConnServ) {
217         // TODO: verify this is correct. Should we identify the context IID with the context UUID??
218         try {
219             // First read connectivity service with connectivity service uuid and update info
220             InstanceIdentifier<ConnectivityService> connServIID =
221                 InstanceIdentifier.builder(Context.class).augmentation(Context1.class)
222                     .child(org.opendaylight.yang.gen.v1.urn
223                         .onf.otcc.yang.tapi.connectivity.rev181210.context.ConnectivityContext.class)
224                     .child(ConnectivityService.class, new ConnectivityServiceKey(updtConnServ.getUuid()))
225                     .build();
226
227             Optional<ConnectivityService> optConnServ =
228                 this.networkTransactionService.read(LogicalDatastoreType.OPERATIONAL, connServIID).get();
229             if (!optConnServ.isPresent()) {
230                 LOG.error("Connection not found in tapi context");
231                 return;
232             }
233             ConnectivityService newConnServ = new ConnectivityServiceBuilder(updtConnServ).build();
234             // merge in datastore
235             this.networkTransactionService.merge(LogicalDatastoreType.OPERATIONAL, connServIID,
236                     newConnServ);
237             this.networkTransactionService.commit().get();
238             LOG.info("TAPI connectivity service merged successfully.");
239         } catch (InterruptedException | ExecutionException e) {
240             LOG.error("Failed to merge TAPI connectivity service", e);
241         }
242     }
243
244     private void deleteConnectivityService(Uuid suuid) {
245         // First read connectivity service with service uuid and update info
246         InstanceIdentifier<ConnectivityService> connectivityServIID =
247             InstanceIdentifier.builder(Context.class).augmentation(Context1.class)
248                 .child(org.opendaylight.yang.gen.v1.urn
249                     .onf.otcc.yang.tapi.connectivity.rev181210.context.ConnectivityContext.class)
250                 .child(ConnectivityService.class, new ConnectivityServiceKey(suuid))
251                 .build();
252         try {
253             this.networkTransactionService.delete(LogicalDatastoreType.OPERATIONAL, connectivityServIID);
254             this.networkTransactionService.commit().get();
255         } catch (InterruptedException | ExecutionException e) {
256             LOG.error("Failed to delete TAPI connectivity service", e);
257         }
258     }
259
260     private void deleteConnection(Uuid connectionUuid) {
261         // First read connectivity service with service uuid and update info
262         InstanceIdentifier<org.opendaylight.yang.gen.v1
263             .urn.onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.Connection> connectionIID =
264             InstanceIdentifier.builder(Context.class).augmentation(Context1.class)
265                 .child(org.opendaylight.yang.gen.v1.urn
266                     .onf.otcc.yang.tapi.connectivity.rev181210.context.ConnectivityContext.class)
267                 .child(org.opendaylight.yang.gen.v1.urn
268                         .onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.Connection.class,
269                     new org.opendaylight.yang.gen.v1.urn
270                         .onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.ConnectionKey(
271                             connectionUuid))
272                 .build();
273         try {
274             this.networkTransactionService.delete(LogicalDatastoreType.OPERATIONAL, connectionIID);
275             this.networkTransactionService.commit().get();
276         } catch (InterruptedException | ExecutionException e) {
277             LOG.error("Failed to delete TAPI connection", e);
278         }
279     }
280
281     public void setServiceUuid(Uuid serviceUuid) {
282         this.serviceUuid = serviceUuid;
283     }
284 }