2 * Copyright © 2021 Nokia, Inc. 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.tapi.listeners;
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;
17 import java.util.Optional;
19 import java.util.UUID;
20 import java.util.concurrent.ExecutionException;
21 import org.opendaylight.mdsal.binding.api.DataBroker;
22 import org.opendaylight.mdsal.binding.api.NotificationPublishService;
23 import org.opendaylight.mdsal.binding.api.NotificationService.CompositeListener;
24 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
25 import org.opendaylight.transportpce.common.network.NetworkTransactionImpl;
26 import org.opendaylight.transportpce.common.network.NetworkTransactionService;
27 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev210915.RendererRpcResultSp;
28 import org.opendaylight.yang.gen.v1.nbi.notifications.rev230728.PublishTapiNotificationService;
29 import org.opendaylight.yang.gen.v1.nbi.notifications.rev230728.PublishTapiNotificationServiceBuilder;
30 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev221121.AdministrativeState;
31 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev221121.Context;
32 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev221121.DateAndTime;
33 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev221121.LifecycleState;
34 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev221121.OperationalState;
35 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev221121.Uuid;
36 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev221121.global._class.Name;
37 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev221121.global._class.NameKey;
38 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev221121.CONNECTIVITYOBJECTTYPECONNECTIVITYSERVICE;
39 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev221121.Context1;
40 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev221121.connectivity.context.ConnectionBuilder;
41 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev221121.connectivity.context.ConnectionKey;
42 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev221121.connectivity.context.ConnectivityService;
43 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev221121.connectivity.context.ConnectivityServiceBuilder;
44 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev221121.connectivity.context.ConnectivityServiceKey;
45 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev221121.connectivity.service.Connection;
46 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.notification.rev221121.NOTIFICATIONTYPEATTRIBUTEVALUECHANGE;
47 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.notification.rev221121.notification.ChangedAttributes;
48 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.notification.rev221121.notification.ChangedAttributesBuilder;
49 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.notification.rev221121.notification.ChangedAttributesKey;
50 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.notification.rev221121.notification.TargetObjectName;
51 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.notification.rev221121.notification.TargetObjectNameBuilder;
52 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.notification.rev221121.notification.TargetObjectNameKey;
53 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
57 public class TapiRendererNotificationHandler {
59 private static final Logger LOG = LoggerFactory.getLogger(TapiRendererNotificationHandler.class);
60 private final DataBroker dataBroker;
61 private Uuid serviceUuid;
62 private RendererRpcResultSp serviceRpcResultSp;
63 private final NetworkTransactionService networkTransactionService;
64 private final NotificationPublishService notificationPublishService;
66 public TapiRendererNotificationHandler(DataBroker dataBroker,
67 NotificationPublishService notificationPublishService) {
68 this.dataBroker = dataBroker;
69 this.networkTransactionService = new NetworkTransactionImpl(this.dataBroker);
70 this.notificationPublishService = notificationPublishService;
73 public CompositeListener getCompositeListener() {
74 return new CompositeListener(Set.of(
75 new CompositeListener.Component<>(RendererRpcResultSp.class, this::onRendererRpcResultSp)));
78 private void onRendererRpcResultSp(RendererRpcResultSp notification) {
79 if (compareServiceRpcResultSp(notification)) {
80 LOG.warn("ServiceRpcResultSp already wired !");
83 serviceRpcResultSp = notification;
84 int notifType = serviceRpcResultSp.getNotificationType().getIntValue();
85 LOG.info("Renderer '{}' Notification received : {}", serviceRpcResultSp.getNotificationType().getName(),
87 /* service-implementation-request. */
89 onServiceImplementationResult(notification);
94 * Process service implementation result for serviceName.
95 * @param notification RendererRpcResultSp
97 private void onServiceImplementationResult(RendererRpcResultSp notification) {
98 switch (serviceRpcResultSp.getStatus()) {
100 if (this.serviceUuid != null) {
101 onSuccededServiceImplementation();
105 onFailedServiceImplementation(notification.getServiceName());
108 LOG.warn("Service Implementation still pending according to RpcStatusEx");
111 LOG.warn("Service Implementation has an unknown RpcStatusEx code");
117 * Process succeeded service implementation for service.
119 private void onSuccededServiceImplementation() {
120 LOG.info("Service implemented !");
121 // TODO: update Connections and Connectivity Service states
122 ConnectivityService connectivityService = getConnectivityService(this.serviceUuid);
123 if (connectivityService == null) {
124 LOG.error("Couldnt retrieve service from datastore");
127 LOG.info("Connectivity service = {}", connectivityService);
128 // TODO --> this throws error because the renderer goes really fast. Is this normal??
129 ConnectivityService updtConnServ = new ConnectivityServiceBuilder(connectivityService)
130 .setAdministrativeState(AdministrativeState.UNLOCKED)
131 .setLifecycleState(LifecycleState.INSTALLED)
132 .setOperationalState(OperationalState.ENABLED)
134 for (Connection connection:updtConnServ.nonnullConnection().values()) {
135 updateConnectionState(connection.getConnectionUuid());
137 updateConnectivityService(updtConnServ);
138 // TODO: need to send notification to kafka in case the topic exists!!
139 sendNbiNotification(createNbiNotification(updtConnServ));
143 * Process failed service implementation for serviceName.
144 * @param serviceName String
146 private void onFailedServiceImplementation(String serviceName) {
147 LOG.error("Renderer implementation failed !");
148 LOG.info("PCE cancel resource done OK !");
149 Uuid suuid = new Uuid(UUID.nameUUIDFromBytes(serviceName.getBytes(StandardCharsets.UTF_8))
151 // get connections of connectivity service and remove them from tapi context and then remove
152 // service from context. The CEPs are maintained as they could be reused by another service
153 ConnectivityService connService = getConnectivityService(suuid);
154 if (connService == null) {
155 LOG.error("Service doesnt exist in tapi context");
158 for (Connection connection:connService.getConnection().values()) {
159 deleteConnection(connection.getConnectionUuid());
161 deleteConnectivityService(suuid);
165 value = "ES_COMPARING_STRINGS_WITH_EQ",
166 justification = "false positives, not strings but real object references comparisons")
167 private Boolean compareServiceRpcResultSp(RendererRpcResultSp notification) {
168 if (serviceRpcResultSp == null) {
171 if (serviceRpcResultSp.getNotificationType() != notification.getNotificationType()) {
174 if (serviceRpcResultSp.getServiceName() != notification.getServiceName()) {
177 if (serviceRpcResultSp.getStatus() != notification.getStatus()) {
180 if (serviceRpcResultSp.getStatusMessage() != notification.getStatusMessage()) {
186 private ConnectivityService getConnectivityService(Uuid suuid) {
187 // TODO: verify this is correct. Should we identify the context IID with the context UUID??
189 // First read connectivity service with service uuid and update info
190 InstanceIdentifier<ConnectivityService> connectivityServIID =
191 InstanceIdentifier.builder(Context.class).augmentation(Context1.class)
192 .child(org.opendaylight.yang.gen.v1.urn
193 .onf.otcc.yang.tapi.connectivity.rev221121.context.ConnectivityContext.class)
194 .child(ConnectivityService.class, new ConnectivityServiceKey(suuid))
197 Optional<ConnectivityService> optConnServ =
198 this.networkTransactionService.read(LogicalDatastoreType.OPERATIONAL, connectivityServIID).get();
199 if (!optConnServ.isPresent()) {
200 LOG.error("Connectivity service not found in tapi context");
203 return optConnServ.orElseThrow();
204 } catch (InterruptedException | ExecutionException e) {
205 LOG.error("Failed to merge TAPI connectivity", e);
210 private void updateConnectionState(Uuid connectionUuid) {
211 // TODO: verify this is correct. Should we identify the context IID with the context UUID??
213 // First read connection with connection uuid and update info
214 InstanceIdentifier<org.opendaylight.yang.gen.v1.urn
215 .onf.otcc.yang.tapi.connectivity.rev221121.connectivity.context.Connection> connectionIID =
216 InstanceIdentifier.builder(Context.class).augmentation(Context1.class)
217 .child(org.opendaylight.yang.gen.v1.urn
218 .onf.otcc.yang.tapi.connectivity.rev221121.context.ConnectivityContext.class)
219 .child(org.opendaylight.yang.gen.v1.urn
220 .onf.otcc.yang.tapi.connectivity.rev221121.connectivity.context.Connection.class,
221 new ConnectionKey(connectionUuid))
224 Optional<org.opendaylight.yang.gen.v1.urn
225 .onf.otcc.yang.tapi.connectivity.rev221121.connectivity.context.Connection> optConn =
226 this.networkTransactionService.read(LogicalDatastoreType.OPERATIONAL, connectionIID).get();
227 if (!optConn.isPresent()) {
228 LOG.error("Connection not found in tapi context");
231 org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev221121.connectivity.context.Connection
232 newConnection = new ConnectionBuilder(optConn.orElseThrow()).setLifecycleState(LifecycleState.INSTALLED)
233 .setOperationalState(OperationalState.ENABLED).build();
234 // merge in datastore
235 this.networkTransactionService.merge(LogicalDatastoreType.OPERATIONAL, connectionIID,
237 this.networkTransactionService.commit().get();
238 LOG.info("TAPI connection merged successfully.");
239 } catch (InterruptedException | ExecutionException e) {
240 LOG.error("Failed to merge TAPI connection", e);
244 private void updateConnectivityService(ConnectivityService updtConnServ) {
245 // TODO: verify this is correct. Should we identify the context IID with the context UUID??
247 // First read connectivity service with connectivity service uuid and update info
248 InstanceIdentifier<ConnectivityService> connServIID =
249 InstanceIdentifier.builder(Context.class).augmentation(Context1.class)
250 .child(org.opendaylight.yang.gen.v1.urn
251 .onf.otcc.yang.tapi.connectivity.rev221121.context.ConnectivityContext.class)
252 .child(ConnectivityService.class, new ConnectivityServiceKey(updtConnServ.getUuid()))
255 Optional<ConnectivityService> optConnServ =
256 this.networkTransactionService.read(LogicalDatastoreType.OPERATIONAL, connServIID).get();
257 if (!optConnServ.isPresent()) {
258 LOG.error("Connection not found in tapi context");
261 ConnectivityService newConnServ = new ConnectivityServiceBuilder(updtConnServ).build();
262 // merge in datastore
263 this.networkTransactionService.merge(LogicalDatastoreType.OPERATIONAL, connServIID,
265 this.networkTransactionService.commit().get();
266 LOG.info("TAPI connectivity service merged successfully.");
267 } catch (InterruptedException | ExecutionException e) {
268 LOG.error("Failed to merge TAPI connectivity service", e);
272 private void deleteConnectivityService(Uuid suuid) {
273 // First read connectivity service with service uuid and update info
274 InstanceIdentifier<ConnectivityService> connectivityServIID =
275 InstanceIdentifier.builder(Context.class).augmentation(Context1.class)
276 .child(org.opendaylight.yang.gen.v1.urn
277 .onf.otcc.yang.tapi.connectivity.rev221121.context.ConnectivityContext.class)
278 .child(ConnectivityService.class, new ConnectivityServiceKey(suuid))
281 this.networkTransactionService.delete(LogicalDatastoreType.OPERATIONAL, connectivityServIID);
282 this.networkTransactionService.commit().get();
283 } catch (InterruptedException | ExecutionException e) {
284 LOG.error("Failed to delete TAPI connectivity service", e);
288 private void deleteConnection(Uuid connectionUuid) {
289 // First read connectivity service with service uuid and update info
290 InstanceIdentifier<org.opendaylight.yang.gen.v1
291 .urn.onf.otcc.yang.tapi.connectivity.rev221121.connectivity.context.Connection> connectionIID =
292 InstanceIdentifier.builder(Context.class).augmentation(Context1.class)
293 .child(org.opendaylight.yang.gen.v1.urn
294 .onf.otcc.yang.tapi.connectivity.rev221121.context.ConnectivityContext.class)
295 .child(org.opendaylight.yang.gen.v1.urn
296 .onf.otcc.yang.tapi.connectivity.rev221121.connectivity.context.Connection.class,
297 new org.opendaylight.yang.gen.v1.urn
298 .onf.otcc.yang.tapi.connectivity.rev221121.connectivity.context.ConnectionKey(
302 this.networkTransactionService.delete(LogicalDatastoreType.OPERATIONAL, connectionIID);
303 this.networkTransactionService.commit().get();
304 } catch (InterruptedException | ExecutionException e) {
305 LOG.error("Failed to delete TAPI connection", e);
309 private void sendNbiNotification(PublishTapiNotificationService service) {
311 this.notificationPublishService.putNotification(service);
312 } catch (InterruptedException e) {
313 LOG.warn("Cannot send notification to nbi", e);
314 Thread.currentThread().interrupt();
318 private PublishTapiNotificationService createNbiNotification(ConnectivityService connService) {
319 if (connService == null) {
320 LOG.error("ConnService is null");
324 Map<ChangedAttributesKey, ChangedAttributes> changedStates = changedAttributesMap.entrySet()
326 .filter(e -> e.getKey().getValueName().equals("administrative")
327 || e.getKey().getValueName().equals("operational"))
328 .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()));
331 Map<ChangedAttributesKey, ChangedAttributes> changedStates = new HashMap<>();
332 changedStates.put(new ChangedAttributesKey("administrativeState"),
333 new ChangedAttributesBuilder()
334 .setNewValue(connService.getAdministrativeState().getName())
335 .setOldValue(AdministrativeState.LOCKED.getName())
336 .setValueName("administrativeState").build());
337 changedStates.put(new ChangedAttributesKey("operationalState"),
338 new ChangedAttributesBuilder()
339 .setNewValue(connService.getOperationalState().getName())
340 .setOldValue(OperationalState.DISABLED.getName())
341 .setValueName("operationalState").build());
342 DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssxxx");
343 OffsetDateTime offsetDateTime = OffsetDateTime.now(ZoneOffset.UTC);
344 DateAndTime datetime = new DateAndTime(dtf.format(offsetDateTime));
345 Map<TargetObjectNameKey, TargetObjectName> targetObjectNames = new HashMap<>();
346 if (connService.getName() != null) {
347 for (Map.Entry<NameKey, Name> entry : connService.getName().entrySet()) {
348 targetObjectNames.put(new TargetObjectNameKey(entry.getKey().getValueName()),
349 new TargetObjectNameBuilder()
350 .setValueName(entry.getValue().getValueName())
351 .setValue(entry.getValue().getValue())
356 return new PublishTapiNotificationServiceBuilder()
357 .setUuid(new Uuid(UUID.randomUUID().toString()))
358 .setTopic(connService.getUuid().getValue())
359 .setTargetObjectIdentifier(connService.getUuid())
360 .setNotificationType(NOTIFICATIONTYPEATTRIBUTEVALUECHANGE.VALUE)
361 .setChangedAttributes(changedStates)
362 .setEventTimeStamp(datetime)
363 .setTargetObjectName(targetObjectNames)
364 .setTargetObjectType(CONNECTIVITYOBJECTTYPECONNECTIVITYSERVICE.VALUE)
365 .setLayerProtocolName(connService.getLayerProtocolName())
369 public void setServiceUuid(Uuid serviceUuid) {
370 this.serviceUuid = serviceUuid;