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.Charset;
12 import java.util.HashMap;
14 import java.util.Optional;
16 import java.util.UUID;
17 import java.util.concurrent.ExecutionException;
18 import org.opendaylight.mdsal.binding.api.DataBroker;
19 import org.opendaylight.mdsal.binding.api.NotificationService.CompositeListener;
20 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
21 import org.opendaylight.transportpce.common.network.NetworkTransactionImpl;
22 import org.opendaylight.transportpce.common.network.NetworkTransactionService;
23 import org.opendaylight.transportpce.tapi.connectivity.ConnectivityUtils;
24 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev240205.ServicePathRpcResult;
25 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev240205.service.path.rpc.result.PathDescription;
26 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev240205.service.path.rpc.result.PathDescriptionBuilder;
27 import org.opendaylight.yang.gen.v1.http.org.transportpce.b.c._interface.service.types.rev220118.RpcStatusEx;
28 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev221121.Context;
29 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.common.rev221121.Uuid;
30 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev221121.Context1;
31 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev221121.CreateConnectivityServiceInput;
32 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev221121.OwnedNodeEdgePoint1;
33 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev221121.OwnedNodeEdgePoint1Builder;
34 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev221121.cep.list.ConnectionEndPoint;
35 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev221121.connectivity.context.ConnectivityService;
36 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev221121.connectivity.context.ConnectivityServiceBuilder;
37 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev221121.connectivity.context.ConnectivityServiceKey;
38 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev221121.connectivity.service.Connection;
39 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev221121.connectivity.service.ConnectionKey;
40 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev221121.context.ConnectivityContextBuilder;
41 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev221121.context.topology.context.topology.node.owned.node.edge.point.CepList;
42 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev221121.context.topology.context.topology.node.owned.node.edge.point.CepListBuilder;
43 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.topology.rev221121.node.OwnedNodeEdgePoint;
44 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.topology.rev221121.node.OwnedNodeEdgePointBuilder;
45 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.topology.rev221121.node.OwnedNodeEdgePointKey;
46 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.topology.rev221121.topology.NodeKey;
47 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.topology.rev221121.topology.context.Topology;
48 import org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.topology.rev221121.topology.context.TopologyKey;
49 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
53 public class TapiPceNotificationHandler {
55 private static final Logger LOG = LoggerFactory.getLogger(TapiPceNotificationHandler.class);
57 private ServicePathRpcResult servicePathRpcResult;
58 private CreateConnectivityServiceInput input;
59 private Uuid serviceUuid;
60 private final DataBroker dataBroker;
61 private final NetworkTransactionService networkTransactionService;
62 private final ConnectivityUtils connectivityUtils;
63 private final Map<org.opendaylight.yang.gen.v1.urn
64 .onf.otcc.yang.tapi.connectivity.rev221121.connectivity.context.ConnectionKey,
65 org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev221121.connectivity.context.Connection>
66 connectionFullMap; // this variable is for complete connection objects
68 public TapiPceNotificationHandler(DataBroker dataBroker, ConnectivityUtils connecUtil) {
69 this.connectionFullMap = new HashMap<>();
70 this.dataBroker = dataBroker;
71 this.networkTransactionService = new NetworkTransactionImpl(this.dataBroker);
72 this.connectivityUtils = connecUtil;
75 public CompositeListener getCompositeListener() {
76 return new CompositeListener(Set.of(
77 new CompositeListener.Component<>(ServicePathRpcResult.class, this::onServicePathRpcResult)));
80 private void onServicePathRpcResult(ServicePathRpcResult notification) {
81 if (compareServicePathRpcResult(notification)) {
82 LOG.warn("ServicePathRpcResult already wired !");
85 servicePathRpcResult = notification;
86 switch (servicePathRpcResult.getNotificationType().getIntValue()) {
87 /* path-computation-request. */
89 onPathComputationResult(notification);
91 /* cancel-resource-reserve. */
93 onCancelResourceResult(notification.getServiceName());
101 * Process path computation request result.
102 * @param notification the result notification.
104 private void onPathComputationResult(ServicePathRpcResult notification) {
105 this.connectionFullMap.clear();
106 LOG.info("PCE '{}' Notification received : {}",servicePathRpcResult.getNotificationType().getName(),
108 if (servicePathRpcResult.getStatus() == RpcStatusEx.Failed) {
109 LOG.error("PCE path computation failed !");
111 } else if (servicePathRpcResult.getStatus() == RpcStatusEx.Pending) {
112 LOG.warn("PCE path computation returned a Penging RpcStatusEx code!");
114 } else if (servicePathRpcResult.getStatus() != RpcStatusEx.Successful) {
115 LOG.error("PCE path computation returned an unknown RpcStatusEx code!");
119 LOG.info("PCE calculation done OK !");
120 if (servicePathRpcResult.getPathDescription() == null) {
121 LOG.error("'PathDescription' parameter is null ");
124 PathDescription pathDescription = new PathDescriptionBuilder()
125 .setAToZDirection(servicePathRpcResult.getPathDescription().getAToZDirection())
126 .setZToADirection(servicePathRpcResult.getPathDescription().getZToADirection())
128 LOG.info("PathDescription for TAPI gets : {}", pathDescription);
130 LOG.error("Input is null !");
133 // TODO: check kind of service: based on the device Id of the input,
134 // verify the type of XPDR and the capacity and determine if it is an OTN service or pure WDM service
135 // Create connections and ceps for the connectivity service.
136 // Connections must be with a locked stated. As the renderer hasnt implemented yet the oc's
137 Map<ConnectionKey, Connection> connectionMap = connectivityUtils.createConnectionsFromService(
138 pathDescription, input.getLayerProtocolName());
139 this.connectionFullMap.putAll(connectivityUtils.getConnectionFullMap());
140 LOG.debug("Connection Map from createConnectionsAndCepsForService is {}, LAYERPROTOCOL of service is {} ",
141 connectionMap.toString(), input.getLayerProtocolName());
142 // add connections to connection context and to connectivity context
143 updateConnectionContextWithConn(this.connectionFullMap, connectionMap, serviceUuid);
147 * Process cancel resource result.
148 * @param serviceName Service name to build uuid.
150 private void onCancelResourceResult(String serviceName) {
151 if (servicePathRpcResult.getStatus() == RpcStatusEx.Failed) {
152 LOG.info("PCE cancel resource failed !");
154 } else if (servicePathRpcResult.getStatus() == RpcStatusEx.Pending) {
155 LOG.warn("PCE cancel returned a Penging RpcStatusEx code!");
157 } else if (servicePathRpcResult.getStatus() != RpcStatusEx.Successful) {
158 LOG.error("PCE cancel returned an unknown RpcStatusEx code!");
161 LOG.info("PCE cancel resource done OK !");
162 Uuid suuid = new Uuid(UUID.nameUUIDFromBytes(serviceName.getBytes(Charset.forName("UTF-8")))
164 // get connections of connectivity service and remove them from tapi context and then remove
165 // service from context. The CEPs are maintained as they could be reused by another service
166 ConnectivityService connService = getConnectivityService(suuid);
167 if (connService == null) {
168 LOG.error("Service doesnt exist in tapi context");
171 for (Connection connection:connService.getConnection().values()) {
172 deleteConnection(connection.getConnectionUuid());
174 deleteConnectivityService(suuid);
178 value = "ES_COMPARING_STRINGS_WITH_EQ",
179 justification = "false positives, not strings but real object references comparisons")
180 private Boolean compareServicePathRpcResult(ServicePathRpcResult notification) {
181 if (servicePathRpcResult == null) {
184 if (servicePathRpcResult.getNotificationType() != notification.getNotificationType()) {
187 if (servicePathRpcResult.getServiceName() != notification.getServiceName()) {
190 if (servicePathRpcResult.getStatus() != notification.getStatus()) {
193 if (servicePathRpcResult.getStatusMessage() != notification.getStatusMessage()) {
200 public void updateTopologyWithCep(Uuid topoUuid, Uuid nodeUuid, Uuid nepUuid, ConnectionEndPoint cep) {
201 // TODO: verify this is correct. Should we identify the context IID with the context UUID??
202 InstanceIdentifier<OwnedNodeEdgePoint> onepIID = InstanceIdentifier.builder(Context.class)
203 .augmentation(org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.topology.rev221121.Context1.class)
204 .child(org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.topology.rev221121.context.TopologyContext.class)
205 .child(Topology.class, new TopologyKey(topoUuid))
206 .child(org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.topology.rev221121.topology.Node.class,
207 new NodeKey(nodeUuid))
208 .child(OwnedNodeEdgePoint.class, new OwnedNodeEdgePointKey(nepUuid))
211 Optional<OwnedNodeEdgePoint> optionalOnep = this.networkTransactionService.read(
212 LogicalDatastoreType.OPERATIONAL, onepIID).get();
213 if (!optionalOnep.isPresent()) {
214 LOG.error("ONEP is not present in datastore");
217 OwnedNodeEdgePoint onep = optionalOnep.orElseThrow();
218 LOG.info("ONEP found = {}", onep.toString());
219 // TODO -> If cep exists -> skip merging to datasore
220 OwnedNodeEdgePoint1 onep1 = onep.augmentation(OwnedNodeEdgePoint1.class);
221 if (onep1 != null && onep1.getCepList() != null && onep1.getCepList().getConnectionEndPoint() != null) {
222 if (onep1.getCepList().getConnectionEndPoint().containsKey(
223 new org.opendaylight.yang.gen.v1
224 .urn.onf.otcc.yang.tapi.connectivity.rev221121.cep.list.ConnectionEndPointKey(cep.key()))) {
225 LOG.info("CEP already in topology, skipping merge");
230 CepList cepList = new CepListBuilder().setConnectionEndPoint(Map.of(cep.key(), cep)).build();
231 OwnedNodeEdgePoint1 onep1Bldr = new OwnedNodeEdgePoint1Builder().setCepList(cepList).build();
232 OwnedNodeEdgePoint newOnep = new OwnedNodeEdgePointBuilder(onep)
233 .addAugmentation(onep1Bldr)
235 LOG.info("New ONEP is {}", newOnep.toString());
236 // merge in datastore
237 this.networkTransactionService.merge(LogicalDatastoreType.OPERATIONAL, onepIID,
239 this.networkTransactionService.commit().get();
240 LOG.info("CEP added successfully.");
241 } catch (InterruptedException | ExecutionException e) {
242 LOG.error("Couldnt update cep in topology", e);
246 public void updateTopologyWithNep(Uuid topoUuid, Uuid nodeUuid, Uuid nepUuid, OwnedNodeEdgePoint onep) {
247 // TODO: verify this is correct. Should we identify the context IID with the context UUID??
248 InstanceIdentifier<OwnedNodeEdgePoint> onepIID = InstanceIdentifier.builder(Context.class)
249 .augmentation(org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.topology.rev221121.Context1.class)
250 .child(org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.topology.rev221121.context.TopologyContext.class)
251 .child(Topology.class, new TopologyKey(topoUuid))
252 .child(org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.topology.rev221121.topology.Node.class,
253 new NodeKey(nodeUuid))
254 .child(OwnedNodeEdgePoint.class, new OwnedNodeEdgePointKey(nepUuid))
257 Optional<OwnedNodeEdgePoint> optionalOnep = this.networkTransactionService.read(
258 LogicalDatastoreType.OPERATIONAL, onepIID).get();
259 if (optionalOnep.isPresent()) {
260 LOG.error("ONEP is already present in datastore");
263 // merge in datastore
264 this.networkTransactionService.merge(LogicalDatastoreType.OPERATIONAL, onepIID,
266 this.networkTransactionService.commit().get();
267 LOG.info("NEP {} added successfully.", onep.getName().toString());
268 } catch (InterruptedException | ExecutionException e) {
269 LOG.error("Couldnt put NEP {} in topology, error = ", onep.getName().toString(), e);
274 private void updateConnectionContextWithConn(
275 Map<org.opendaylight.yang.gen.v1.urn
276 .onf.otcc.yang.tapi.connectivity.rev221121.connectivity.context.ConnectionKey,
277 org.opendaylight.yang.gen.v1.urn
278 .onf.otcc.yang.tapi.connectivity.rev221121.connectivity.context.Connection> connFullMap,
279 Map<ConnectionKey, Connection> connMap, Uuid suuid) {
280 // TODO: verify this is correct. Should we identify the context IID with the context UUID??
282 ConnectivityService connServ = getConnectivityService(suuid);
283 ConnectivityService updtConnServ = new ConnectivityServiceBuilder(connServ)
284 .setConnection(connMap)
287 // Perform the merge operation with the new conn service and the connection context updated
288 org.opendaylight.yang.gen.v1.urn.onf.otcc.yang.tapi.connectivity.rev221121.context.ConnectivityContext
289 connectivityContext = new ConnectivityContextBuilder()
290 .setConnectivityService(Map.of(updtConnServ.key(), updtConnServ))
291 .setConnection(connFullMap)
293 InstanceIdentifier<org.opendaylight.yang.gen.v1.urn
294 .onf.otcc.yang.tapi.connectivity.rev221121.context.ConnectivityContext> connectivitycontextIID =
295 InstanceIdentifier.builder(Context.class).augmentation(Context1.class)
296 .child(org.opendaylight.yang.gen.v1.urn
297 .onf.otcc.yang.tapi.connectivity.rev221121.context.ConnectivityContext.class)
299 // merge in datastore
300 this.networkTransactionService.merge(LogicalDatastoreType.OPERATIONAL, connectivitycontextIID,
301 connectivityContext);
302 this.networkTransactionService.commit().get();
303 LOG.info("TAPI connectivity merged successfully.");
304 } catch (InterruptedException | ExecutionException e) {
305 LOG.error("Failed to merge TAPI connectivity", e);
309 private ConnectivityService getConnectivityService(Uuid suuid) {
311 // First read connectivity service with service uuid and update info
312 InstanceIdentifier<ConnectivityService> connectivityServIID =
313 InstanceIdentifier.builder(Context.class).augmentation(Context1.class)
314 .child(org.opendaylight.yang.gen.v1.urn
315 .onf.otcc.yang.tapi.connectivity.rev221121.context.ConnectivityContext.class)
316 .child(ConnectivityService.class, new ConnectivityServiceKey(suuid))
319 Optional<ConnectivityService> optConnServ =
320 this.networkTransactionService.read(LogicalDatastoreType.OPERATIONAL, connectivityServIID).get();
321 if (optConnServ.isEmpty()) {
322 LOG.error("Connectivity service not found in tapi context");
325 return optConnServ.orElseThrow();
326 } catch (InterruptedException | ExecutionException e) {
327 LOG.error("Connectivity service not found in tapi context. Error:", e);
332 private void deleteConnectivityService(Uuid suuid) {
333 // First read connectivity service with service uuid and update info
334 InstanceIdentifier<ConnectivityService> connectivityServIID =
335 InstanceIdentifier.builder(Context.class).augmentation(Context1.class)
336 .child(org.opendaylight.yang.gen.v1.urn
337 .onf.otcc.yang.tapi.connectivity.rev221121.context.ConnectivityContext.class)
338 .child(ConnectivityService.class, new ConnectivityServiceKey(suuid))
341 this.networkTransactionService.delete(LogicalDatastoreType.OPERATIONAL, connectivityServIID);
342 this.networkTransactionService.commit().get();
343 } catch (InterruptedException | ExecutionException e) {
344 LOG.error("Failed to delete TAPI connectivity service", e);
348 private void deleteConnection(Uuid connectionUuid) {
349 // First read connectivity service with service uuid and update info
350 InstanceIdentifier<org.opendaylight.yang.gen.v1
351 .urn.onf.otcc.yang.tapi.connectivity.rev221121.connectivity.context.Connection> connectionIID =
352 InstanceIdentifier.builder(Context.class).augmentation(Context1.class)
353 .child(org.opendaylight.yang.gen.v1.urn
354 .onf.otcc.yang.tapi.connectivity.rev221121.context.ConnectivityContext.class)
355 .child(org.opendaylight.yang.gen.v1.urn
356 .onf.otcc.yang.tapi.connectivity.rev221121.connectivity.context.Connection.class,
357 new org.opendaylight.yang.gen.v1.urn
358 .onf.otcc.yang.tapi.connectivity.rev221121.connectivity.context.ConnectionKey(
362 this.networkTransactionService.delete(LogicalDatastoreType.OPERATIONAL, connectionIID);
363 this.networkTransactionService.commit().get();
364 } catch (InterruptedException | ExecutionException e) {
365 LOG.error("Failed to delete TAPI connection", e);
369 public void setInput(CreateConnectivityServiceInput input) {
373 public void setServiceUuid(Uuid serviceUuid) {
374 this.serviceUuid = serviceUuid;