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;
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.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev210915.RendererRpcResultSp;
26 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.renderer.rev210915.TransportpceRendererListener;
27 import org.opendaylight.yang.gen.v1.nbi.notifications.rev211013.PublishTapiNotificationService;
28 import org.opendaylight.yang.gen.v1.nbi.notifications.rev211013.PublishTapiNotificationServiceBuilder;
29 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev181210.AdministrativeState;
30 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev181210.Context;
31 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev181210.DateAndTime;
32 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev181210.LifecycleState;
33 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev181210.OperationalState;
34 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev181210.Uuid;
35 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev181210.global._class.Name;
36 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev181210.global._class.NameKey;
37 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev181210.Context1;
38 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.ConnectionBuilder;
39 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.ConnectionKey;
40 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.ConnectivityService;
41 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.ConnectivityServiceBuilder;
42 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.ConnectivityServiceKey;
43 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev181210.connectivity.service.Connection;
44 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.notification.rev181210.NotificationType;
45 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.notification.rev181210.ObjectType;
46 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.notification.rev181210.notification.ChangedAttributes;
47 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.notification.rev181210.notification.ChangedAttributesBuilder;
48 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.notification.rev181210.notification.ChangedAttributesKey;
49 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.notification.rev181210.notification.TargetObjectName;
50 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.notification.rev181210.notification.TargetObjectNameBuilder;
51 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.notification.rev181210.notification.TargetObjectNameKey;
52 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
56 public class TapiRendererListenerImpl implements TransportpceRendererListener {
58 private static final Logger LOG = LoggerFactory.getLogger(TapiRendererListenerImpl.class);
59 private final DataBroker dataBroker;
60 private Uuid serviceUuid;
61 private RendererRpcResultSp serviceRpcResultSp;
62 private final NetworkTransactionService networkTransactionService;
63 private final NotificationPublishService notificationPublishService;
65 public TapiRendererListenerImpl(DataBroker dataBroker, NotificationPublishService notificationPublishService) {
66 this.dataBroker = dataBroker;
67 this.networkTransactionService = new NetworkTransactionImpl(this.dataBroker);
68 this.notificationPublishService = notificationPublishService;
72 public void onRendererRpcResultSp(RendererRpcResultSp notification) {
73 if (compareServiceRpcResultSp(notification)) {
74 LOG.warn("ServiceRpcResultSp already wired !");
77 serviceRpcResultSp = notification;
78 int notifType = serviceRpcResultSp.getNotificationType().getIntValue();
79 LOG.info("Renderer '{}' Notification received : {}", serviceRpcResultSp.getNotificationType().getName(),
81 /* service-implementation-request. */
83 onServiceImplementationResult(notification);
88 * Process service implementation result for serviceName.
89 * @param notification RendererRpcResultSp
91 private void onServiceImplementationResult(RendererRpcResultSp notification) {
92 switch (serviceRpcResultSp.getStatus()) {
94 if (this.serviceUuid != null) {
95 onSuccededServiceImplementation();
99 onFailedServiceImplementation(notification.getServiceName());
102 LOG.warn("Service Implementation still pending according to RpcStatusEx");
105 LOG.warn("Service Implementation has an unknown RpcStatusEx code");
111 * Process succeeded service implementation for service.
113 private void onSuccededServiceImplementation() {
114 LOG.info("Service implemented !");
115 // TODO: update Connections and Connectivity Service states
116 ConnectivityService connectivityService = getConnectivityService(this.serviceUuid);
117 if (connectivityService == null) {
118 LOG.error("Couldnt retrieve service from datastore");
121 LOG.info("Connectivity service = {}", connectivityService);
122 // TODO --> this throws error because the renderer goes really fast. Is this normal??
123 ConnectivityService updtConnServ = new ConnectivityServiceBuilder(connectivityService)
124 .setAdministrativeState(AdministrativeState.UNLOCKED)
125 .setLifecycleState(LifecycleState.INSTALLED)
126 .setOperationalState(OperationalState.ENABLED)
128 for (Connection connection:updtConnServ.nonnullConnection().values()) {
129 updateConnectionState(connection.getConnectionUuid());
131 updateConnectivityService(updtConnServ);
132 // TODO: need to send notification to kafka in case the topic exists!!
133 sendNbiNotification(createNbiNotification(updtConnServ));
137 * Process failed service implementation for serviceName.
138 * @param serviceName String
140 private void onFailedServiceImplementation(String serviceName) {
141 LOG.error("Renderer implementation failed !");
142 LOG.info("PCE cancel resource done OK !");
143 Uuid suuid = new Uuid(UUID.nameUUIDFromBytes(serviceName.getBytes(StandardCharsets.UTF_8))
145 // get connections of connectivity service and remove them from tapi context and then remove
146 // service from context. The CEPs are maintained as they could be reused by another service
147 ConnectivityService connService = getConnectivityService(suuid);
148 if (connService == null) {
149 LOG.error("Service doesnt exist in tapi context");
152 for (Connection connection:connService.getConnection().values()) {
153 deleteConnection(connection.getConnectionUuid());
155 deleteConnectivityService(suuid);
159 value = "ES_COMPARING_STRINGS_WITH_EQ",
160 justification = "false positives, not strings but real object references comparisons")
161 private Boolean compareServiceRpcResultSp(RendererRpcResultSp notification) {
162 if (serviceRpcResultSp == null) {
165 if (serviceRpcResultSp.getNotificationType() != notification.getNotificationType()) {
168 if (serviceRpcResultSp.getServiceName() != notification.getServiceName()) {
171 if (serviceRpcResultSp.getStatus() != notification.getStatus()) {
174 if (serviceRpcResultSp.getStatusMessage() != notification.getStatusMessage()) {
180 private ConnectivityService getConnectivityService(Uuid suuid) {
181 // TODO: verify this is correct. Should we identify the context IID with the context UUID??
183 // First read connectivity service with service uuid and update info
184 InstanceIdentifier<ConnectivityService> connectivityServIID =
185 InstanceIdentifier.builder(Context.class).augmentation(Context1.class)
186 .child(org.opendaylight.yang.gen.v1.urn
187 .onf.otcc.yang.tapi.connectivity.rev181210.context.ConnectivityContext.class)
188 .child(ConnectivityService.class, new ConnectivityServiceKey(suuid))
191 Optional<ConnectivityService> optConnServ =
192 this.networkTransactionService.read(LogicalDatastoreType.OPERATIONAL, connectivityServIID).get();
193 if (!optConnServ.isPresent()) {
194 LOG.error("Connectivity service not found in tapi context");
197 return optConnServ.get();
198 } catch (InterruptedException | ExecutionException e) {
199 LOG.error("Failed to merge TAPI connectivity", e);
204 private void updateConnectionState(Uuid connectionUuid) {
205 // TODO: verify this is correct. Should we identify the context IID with the context UUID??
207 // First read connection with connection uuid and update info
208 InstanceIdentifier<org.opendaylight.yang.gen.v1.urn
209 .onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.Connection> connectionIID =
210 InstanceIdentifier.builder(Context.class).augmentation(Context1.class)
211 .child(org.opendaylight.yang.gen.v1.urn
212 .onf.otcc.yang.tapi.connectivity.rev181210.context.ConnectivityContext.class)
213 .child(org.opendaylight.yang.gen.v1.urn
214 .onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.Connection.class,
215 new ConnectionKey(connectionUuid))
218 Optional<org.opendaylight.yang.gen.v1.urn
219 .onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.Connection> optConn =
220 this.networkTransactionService.read(LogicalDatastoreType.OPERATIONAL, connectionIID).get();
221 if (!optConn.isPresent()) {
222 LOG.error("Connection not found in tapi context");
225 org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.Connection
226 newConnection = new ConnectionBuilder(optConn.get()).setLifecycleState(LifecycleState.INSTALLED)
227 .setOperationalState(OperationalState.ENABLED).build();
228 // merge in datastore
229 this.networkTransactionService.merge(LogicalDatastoreType.OPERATIONAL, connectionIID,
231 this.networkTransactionService.commit().get();
232 LOG.info("TAPI connection merged successfully.");
233 } catch (InterruptedException | ExecutionException e) {
234 LOG.error("Failed to merge TAPI connection", e);
238 private void updateConnectivityService(ConnectivityService updtConnServ) {
239 // TODO: verify this is correct. Should we identify the context IID with the context UUID??
241 // First read connectivity service with connectivity service uuid and update info
242 InstanceIdentifier<ConnectivityService> connServIID =
243 InstanceIdentifier.builder(Context.class).augmentation(Context1.class)
244 .child(org.opendaylight.yang.gen.v1.urn
245 .onf.otcc.yang.tapi.connectivity.rev181210.context.ConnectivityContext.class)
246 .child(ConnectivityService.class, new ConnectivityServiceKey(updtConnServ.getUuid()))
249 Optional<ConnectivityService> optConnServ =
250 this.networkTransactionService.read(LogicalDatastoreType.OPERATIONAL, connServIID).get();
251 if (!optConnServ.isPresent()) {
252 LOG.error("Connection not found in tapi context");
255 ConnectivityService newConnServ = new ConnectivityServiceBuilder(updtConnServ).build();
256 // merge in datastore
257 this.networkTransactionService.merge(LogicalDatastoreType.OPERATIONAL, connServIID,
259 this.networkTransactionService.commit().get();
260 LOG.info("TAPI connectivity service merged successfully.");
261 } catch (InterruptedException | ExecutionException e) {
262 LOG.error("Failed to merge TAPI connectivity service", e);
266 private void deleteConnectivityService(Uuid suuid) {
267 // First read connectivity service with service uuid and update info
268 InstanceIdentifier<ConnectivityService> connectivityServIID =
269 InstanceIdentifier.builder(Context.class).augmentation(Context1.class)
270 .child(org.opendaylight.yang.gen.v1.urn
271 .onf.otcc.yang.tapi.connectivity.rev181210.context.ConnectivityContext.class)
272 .child(ConnectivityService.class, new ConnectivityServiceKey(suuid))
275 this.networkTransactionService.delete(LogicalDatastoreType.OPERATIONAL, connectivityServIID);
276 this.networkTransactionService.commit().get();
277 } catch (InterruptedException | ExecutionException e) {
278 LOG.error("Failed to delete TAPI connectivity service", e);
282 private void deleteConnection(Uuid connectionUuid) {
283 // First read connectivity service with service uuid and update info
284 InstanceIdentifier<org.opendaylight.yang.gen.v1
285 .urn.onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.Connection> connectionIID =
286 InstanceIdentifier.builder(Context.class).augmentation(Context1.class)
287 .child(org.opendaylight.yang.gen.v1.urn
288 .onf.otcc.yang.tapi.connectivity.rev181210.context.ConnectivityContext.class)
289 .child(org.opendaylight.yang.gen.v1.urn
290 .onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.Connection.class,
291 new org.opendaylight.yang.gen.v1.urn
292 .onf.otcc.yang.tapi.connectivity.rev181210.connectivity.context.ConnectionKey(
296 this.networkTransactionService.delete(LogicalDatastoreType.OPERATIONAL, connectionIID);
297 this.networkTransactionService.commit().get();
298 } catch (InterruptedException | ExecutionException e) {
299 LOG.error("Failed to delete TAPI connection", e);
303 private void sendNbiNotification(PublishTapiNotificationService service) {
305 this.notificationPublishService.putNotification(service);
306 } catch (InterruptedException e) {
307 LOG.warn("Cannot send notification to nbi", e);
308 Thread.currentThread().interrupt();
312 private PublishTapiNotificationService createNbiNotification(ConnectivityService connService) {
313 if (connService == null) {
314 LOG.error("ConnService is null");
318 Map<ChangedAttributesKey, ChangedAttributes> changedStates = changedAttributesMap.entrySet()
320 .filter(e -> e.getKey().getValueName().equals("administrative")
321 || e.getKey().getValueName().equals("operational"))
322 .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()));
325 Map<ChangedAttributesKey, ChangedAttributes> changedStates = new HashMap<>();
326 changedStates.put(new ChangedAttributesKey("administrativeState"),
327 new ChangedAttributesBuilder()
328 .setNewValue(connService.getAdministrativeState().getName())
329 .setOldValue(AdministrativeState.LOCKED.getName())
330 .setValueName("administrativeState").build());
331 changedStates.put(new ChangedAttributesKey("operationalState"),
332 new ChangedAttributesBuilder()
333 .setNewValue(connService.getOperationalState().getName())
334 .setOldValue(OperationalState.DISABLED.getName())
335 .setValueName("operationalState").build());
336 DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssxxx");
337 OffsetDateTime offsetDateTime = OffsetDateTime.now(ZoneOffset.UTC);
338 DateAndTime datetime = new DateAndTime(dtf.format(offsetDateTime));
339 Map<TargetObjectNameKey, TargetObjectName> targetObjectNames = new HashMap<>();
340 if (connService.getName() != null) {
341 for (Map.Entry<NameKey, Name> entry : connService.getName().entrySet()) {
342 targetObjectNames.put(new TargetObjectNameKey(entry.getKey().getValueName()),
343 new TargetObjectNameBuilder()
344 .setValueName(entry.getValue().getValueName())
345 .setValue(entry.getValue().getValue())
350 return new PublishTapiNotificationServiceBuilder()
351 .setUuid(new Uuid(UUID.randomUUID().toString()))
352 .setTopic(connService.getUuid().getValue())
353 .setTargetObjectIdentifier(connService.getUuid())
354 .setNotificationType(NotificationType.ATTRIBUTEVALUECHANGE)
355 .setChangedAttributes(changedStates)
356 .setEventTimeStamp(datetime)
357 .setTargetObjectName(targetObjectNames)
358 .setTargetObjectType(ObjectType.CONNECTIVITYSERVICE)
359 .setLayerProtocolName(connService.getServiceLayer())
363 public void setServiceUuid(Uuid serviceUuid) {
364 this.serviceUuid = serviceUuid;