Refactoring rollback when service create fails 30/109830/6
authorJoakim Törnqvist <joakim.tornqvist@smartoptics.com>
Wed, 17 Jan 2024 14:35:53 +0000 (14:35 +0000)
committerJoakim Törnqvist <joakim.tornqvist@smartoptics.com>
Tue, 13 Feb 2024 12:58:35 +0000 (12:58 +0000)
The new rollback feature is primarily implemented in the method
deviceRendering in the class RendererServiceOperationsImpl.

The new class TransactionHistory is passed into
DeviceRendererServiceImpl via DeviceRenderingTask.
DeviceRendererServiceImpl populates the TransationHistory class
with created interfaces etc.

A new rollback task NetworkDeviceRenderingRollbackTask has been
created intended to replace DeviceRenderingRollbackTask. The
class is instantiated with a TransactionHistory object intended
to be used in case of a rollback.

Sample pseudo code extract

class RendererServiceOperationsImpl:
private List<DeviceRenderingResult> deviceRendering(...) {

    History transactionHistory = new TransactionHistory();

    ListenableFuture<DeviceRenderingResult> atozrenderingFuture =
        this.executor.submit(
            new DeviceRenderingTask(..., transactionHistory));

    ListenableFuture<DeviceRenderingResult> ztoarenderingFuture =
        this.executor.submit(
            new DeviceRenderingTask(..., transactionHistory));

    ListenableFuture<List<DeviceRenderingResult>>
        renderingCombinedFuture = Futures.allAsList(
            atozrenderingFuture, ztoarenderingFuture
        );

    List<DeviceRenderingResult> renderingResults = new ArrayList<>(2);
    renderingResults = renderingCombinedFuture.get(...);

    rollbackProcessor.addTask(
        new NetworkDeviceRenderingRollbackTask(
            "RollbackTransactionHistoryTask",
            transactionHistory,
            ! (renderingResults.get(0).isSuccess()
                && renderingResults.get(1).isSuccess()),
            deviceRenderer,
            new RollbackResultMessage()
        )
    );

    return renderingResults;
}

New somewhat simplified rollback method using TransactionHistory
intended to replace the previous method using RendererRollbackInput:

class RendererServiceOperationsImpl:
@Override
public RendererRollbackOutput rendererRollback(
    History transactionHistory
) {
    LOG.info("Rolling back...");

    Result rollbackResult = new FailedRollbackResult();
    Subscriber deleteSubscriber = new DeleteSubscriber(rollbackResult);

    transactionHistory.rollback(
            new DeleteService(
                    crossConnect,
                    openRoadmInterfaces,
                    deleteSubscriber
            )
    );

    LOG.info("Rollback done!");

    return rollbackResult.renderRollbackOutput();
}

JIRA: TRNSPRTPCE-615
Change-Id: Ib795f077ae763bd92bac44ee77186a19ed0e4b2a
Signed-off-by: Joakim Törnqvist <joakim.tornqvist@smartoptics.com>
renderer/src/main/java/org/opendaylight/transportpce/renderer/provisiondevice/DeviceRendererService.java
renderer/src/main/java/org/opendaylight/transportpce/renderer/provisiondevice/DeviceRendererServiceImpl.java
renderer/src/main/java/org/opendaylight/transportpce/renderer/provisiondevice/RendererServiceOperationsImpl.java
renderer/src/main/java/org/opendaylight/transportpce/renderer/provisiondevice/tasks/DeviceRenderingTask.java
renderer/src/main/java/org/opendaylight/transportpce/renderer/provisiondevice/tasks/NetworkDeviceRenderingRollbackTask.java [new file with mode: 0644]
renderer/src/main/java/org/opendaylight/transportpce/renderer/provisiondevice/tasks/ResultMessage.java [new file with mode: 0644]
renderer/src/main/java/org/opendaylight/transportpce/renderer/provisiondevice/tasks/RollbackResultMessage.java [new file with mode: 0644]
renderer/src/test/java/org/opendaylight/transportpce/renderer/provisiondevice/RendererServiceOperationsImplTest.java

index a242147bdcdfffe2e4cb4d15a8fa7addc3e097d6..049247b45dc17f8e0d8a541c92a26f5de9a6c88d 100644 (file)
@@ -10,6 +10,7 @@ package org.opendaylight.transportpce.renderer.provisiondevice;
 
 import org.opendaylight.transportpce.common.openroadminterfaces.OpenRoadmInterfaceException;
 import org.opendaylight.transportpce.renderer.provisiondevice.servicepath.ServicePathDirection;
+import org.opendaylight.transportpce.renderer.provisiondevice.transaction.history.History;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.device.renderer.rev211004.CreateOtsOmsInput;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.device.renderer.rev211004.CreateOtsOmsOutput;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.device.renderer.rev211004.RendererRollbackInput;
@@ -44,6 +45,35 @@ public interface DeviceRendererService {
      */
     ServicePathOutput setupServicePath(ServicePathInput input, ServicePathDirection direction);
 
+    /**
+     * This method set's wavelength path based on following steps.
+     *
+     * <p>
+     * For each node:
+     * 1. Create Och interface on source termination point.
+     * 2. Create Och interface on destination termination point.
+     * 3. Create cross connect between source and destination tps created in step 1
+     *    and 2.
+     *
+     * Naming convention used for OCH interfaces name : tp-wavenumber Naming
+     * convention used for cross connect name : src-dest-wavenumber
+     * </p>
+     *
+     * @param input
+     *            Input parameter from the service-path yang model
+     * @param direction
+     *            Service Path direction
+     * @param transactionHistory
+     *            Object tracking created interface(s) and connection(s).
+     *
+     * @return Result list of all nodes if request successful otherwise specific
+     *         reason of failure.
+     */
+    ServicePathOutput setupServicePath(
+            ServicePathInput input,
+            ServicePathDirection direction,
+            History transactionHistory);
+
     /**
      * This method removes wavelength path based on following steps.
      *
@@ -72,6 +102,14 @@ public interface DeviceRendererService {
      */
     RendererRollbackOutput rendererRollback(RendererRollbackInput input);
 
+    /**
+     * Rollback created interfaces and cross connects specified by transaction history.
+     *
+     * @param transactionHistory The transaction history in need of rollback.
+     * @return Success flag and nodes which failed to rollback
+     */
+    RendererRollbackOutput rendererRollback(History transactionHistory);
+
     /**
      * This method creates the basis of ots and oms interfaces on a specific ROADM degree.
      *
index 2ffa8be2598540b41541a52d4276b223cacf7004..7c204f255903f961d56d100e31c743225e37454f 100644 (file)
@@ -49,6 +49,15 @@ import org.opendaylight.transportpce.common.openroadminterfaces.OpenRoadmInterfa
 import org.opendaylight.transportpce.renderer.openroadminterface.OpenRoadmInterfaceFactory;
 import org.opendaylight.transportpce.renderer.provisiondevice.servicepath.ServiceListTopology;
 import org.opendaylight.transportpce.renderer.provisiondevice.servicepath.ServicePathDirection;
+import org.opendaylight.transportpce.renderer.provisiondevice.transaction.Connection;
+import org.opendaylight.transportpce.renderer.provisiondevice.transaction.DeviceInterface;
+import org.opendaylight.transportpce.renderer.provisiondevice.transaction.delete.DeleteService;
+import org.opendaylight.transportpce.renderer.provisiondevice.transaction.delete.DeleteSubscriber;
+import org.opendaylight.transportpce.renderer.provisiondevice.transaction.delete.FailedRollbackResult;
+import org.opendaylight.transportpce.renderer.provisiondevice.transaction.delete.Result;
+import org.opendaylight.transportpce.renderer.provisiondevice.transaction.delete.Subscriber;
+import org.opendaylight.transportpce.renderer.provisiondevice.transaction.history.History;
+import org.opendaylight.transportpce.renderer.provisiondevice.transaction.history.NonStickHistoryMemory;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.alarmsuppression.rev171102.ServiceNodelist;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.alarmsuppression.rev171102.service.nodelist.NodelistBuilder;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.alarmsuppression.rev171102.service.nodelist.NodelistKey;
@@ -113,12 +122,21 @@ public class DeviceRendererServiceImpl implements DeviceRendererService {
         this.openRoadmInterfaceFactory = new OpenRoadmInterfaceFactory(mappingUtils, portMapping, openRoadmInterfaces);
     }
 
+    @Override
+    public ServicePathOutput setupServicePath(ServicePathInput input, ServicePathDirection direction) {
+        return setupServicePath(input, direction, new NonStickHistoryMemory());
+    }
+
     @SuppressWarnings("rawtypes")
     // FIXME check if the ForkJoinTask raw type can be avoided
     // Raw types use are discouraged since they lack type safety.
     // Resulting Problems are observed at run time and not at compile time
     @Override
-    public ServicePathOutput setupServicePath(ServicePathInput input, ServicePathDirection direction) {
+    public ServicePathOutput setupServicePath(
+            ServicePathInput input,
+            ServicePathDirection direction,
+            History transactionHistory
+    ) {
         LOG.info("setup service path for input {} and direction {}", input, direction);
         List<Nodes> nodes = new ArrayList<>();
         if (input.getNodes() != null) {
@@ -164,10 +182,14 @@ public class DeviceRendererServiceImpl implements DeviceRendererService {
                         crossConnectFlag++;
                         String supportingOchInterface = this.openRoadmInterfaceFactory.createOpenRoadmOchInterface(
                                 nodeId, destTp, spectrumInformation);
+                        transactionHistory.add(new DeviceInterface(nodeId, supportingOchInterface));
+
                         // Split the string based on # pass the last element as the supported Interface
                         // This is needed for 7.1 device models with B100G, we have OTSI, OTSI-group combined as OCH
                         String[] listOfSuppOchInf = supportingOchInterface.split("#");
                         List<String> createdOchInf = Arrays.asList(listOfSuppOchInf);
+                        transactionHistory.addInterfaces(nodeId, listOfSuppOchInf);
+
                         createdOchInterfaces.addAll(createdOchInf);
                         LOG.info("DEST all otsi interfaces {}", createdOchInterfaces);
                         // Taking the last element
@@ -176,6 +198,8 @@ public class DeviceRendererServiceImpl implements DeviceRendererService {
                                 .createOpenRoadmOtu4Interface(nodeId, destTp, supportingOchInterface, apiInfoA,
                                         apiInfoZ);
                         createdOtuInterfaces.add(supportingOtuInterface);
+                        transactionHistory.add(new DeviceInterface(nodeId, supportingOtuInterface));
+
                         LOG.info("all dest otu interfaces {}", createdOtuInterfaces);
                         if (srcTp == null) {
                             otnLinkTps.add(new LinkTpBuilder().setNodeId(nodeId).setTpId(destTp).build());
@@ -183,23 +207,32 @@ public class DeviceRendererServiceImpl implements DeviceRendererService {
                             // If src and dest tp contains the network token, then it is regenerator
                             LOG.info("Create the ODUCn for regen on the dest-tp");
                             // Here we first create ODUCn interface for the Regen
-                            createdOduInterfaces.add(this.openRoadmInterfaceFactory
-                                    .createOpenRoadmOducn(nodeId, destTp));
+                            String openRoadmOducn = this.openRoadmInterfaceFactory
+                                    .createOpenRoadmOducn(nodeId, destTp);
+                            createdOduInterfaces.add(openRoadmOducn);
+                            transactionHistory.addInterfaces(nodeId, openRoadmOducn);
+
                             LOG.info("all dest odu interfaces {}", createdOduInterfaces);
                         } else {
                             // This is needed for 7.1 device models for 400GE, since we have ODUC4 and ODUflex
                             // are combined
-                            createdOduInterfaces = Set.of(this.openRoadmInterfaceFactory
-                                .createOpenRoadmOdu4HOInterface(
-                                    nodeId, destTp, false, apiInfoA, apiInfoZ, PT_07).split("#"));
+                            String[] oduInterfaces = this.openRoadmInterfaceFactory
+                                    .createOpenRoadmOdu4HOInterface(
+                                            nodeId, destTp, false, apiInfoA, apiInfoZ, PT_07).split("#");
+                            createdOduInterfaces.addAll(Arrays.asList(oduInterfaces));
+                            transactionHistory.addInterfaces(nodeId, oduInterfaces);
+
                         }
                     }
                     if ((srcTp != null) && srcTp.contains(StringConstants.CLIENT_TOKEN)) {
                         LOG.info("Adding supporting EThernet interface for node {}, src tp {}", nodeId, srcTp);
                         crossConnectFlag++;
                         // create OpenRoadm Xponder Client Interfaces
-                        createdEthInterfaces.add(this.openRoadmInterfaceFactory.createOpenRoadmEthInterface(
-                                nodeId, srcTp));
+                        String openRoadmEthInterface = this.openRoadmInterfaceFactory.createOpenRoadmEthInterface(
+                                nodeId, srcTp);
+                        createdEthInterfaces.add(openRoadmEthInterface);
+                        transactionHistory.add(new DeviceInterface(nodeId, openRoadmEthInterface));
+
                     }
                     if ((srcTp != null) && srcTp.contains(StringConstants.NETWORK_TOKEN)) {
                         LOG.info("Adding supporting OCH interface for node {}, src tp {}, spectrumInformation {}",
@@ -208,10 +241,14 @@ public class DeviceRendererServiceImpl implements DeviceRendererService {
                         // create OpenRoadm Xponder Line Interfaces
                         String supportingOchInterface = this.openRoadmInterfaceFactory.createOpenRoadmOchInterface(
                                 nodeId, srcTp, spectrumInformation);
+                        transactionHistory.add(new DeviceInterface(nodeId, supportingOchInterface));
+
                         // createdOchInterfaces.add(supportingOchInterface);
                         // Split the string based on # pass the last element as the supported Interface
                         // This is needed for 7.1 device models with B100G, we have OTSI, OTSI-group combined as OCH
                         String[] listOfSuppOchInf = supportingOchInterface.split("#");
+                        transactionHistory.addInterfaces(nodeId, listOfSuppOchInf);
+
                         List<String> tmpCreatedOchInterfaces = Arrays.asList(listOfSuppOchInf);
                         createdOchInterfaces.addAll(tmpCreatedOchInterfaces);
                         // Taking the last element
@@ -219,40 +256,53 @@ public class DeviceRendererServiceImpl implements DeviceRendererService {
                         String supportingOtuInterface = this.openRoadmInterfaceFactory.createOpenRoadmOtu4Interface(
                                 nodeId, srcTp, supportingOchInterface, apiInfoA, apiInfoZ);
                         createdOtuInterfaces.add(supportingOtuInterface);
+                        transactionHistory.add(new DeviceInterface(nodeId, supportingOtuInterface));
+
                         if (destTp == null) {
                             otnLinkTps.add(new LinkTpBuilder().setNodeId(nodeId).setTpId(srcTp).build());
                         } else if (destTp.contains(StringConstants.NETWORK_TOKEN)) {
                             // If the src and dest tp have network-token, then it is a regen
                             LOG.info("Create the regen-interfaces on the src-tp");
                             // Here we first create ODUCn interface for the Regen
-                            createdOduInterfaces.add(this.openRoadmInterfaceFactory.createOpenRoadmOducn(nodeId,
-                                    srcTp));
+                            String openRoadmOducn = this.openRoadmInterfaceFactory.createOpenRoadmOducn(nodeId,
+                                    srcTp);
+                            createdOduInterfaces.add(openRoadmOducn);
+                            transactionHistory.add(new DeviceInterface(nodeId, openRoadmOducn));
+
                             LOG.info("all src odu interfaces {}", createdOduInterfaces);
                         } else {
-                            createdOduInterfaces.add(this.openRoadmInterfaceFactory.createOpenRoadmOdu4HOInterface(
-                                    nodeId, srcTp, false, apiInfoA, apiInfoZ, PT_07));
+                            String openRoadmOdu4HOInterface = this.openRoadmInterfaceFactory
+                                    .createOpenRoadmOdu4HOInterface(nodeId, srcTp, false, apiInfoA, apiInfoZ, PT_07);
+                            createdOduInterfaces.add(openRoadmOdu4HOInterface);
+                            transactionHistory.add(new DeviceInterface(nodeId, openRoadmOdu4HOInterface));
                         }
                     }
                     if ((destTp != null) && destTp.contains(StringConstants.CLIENT_TOKEN)) {
                         LOG.info("Adding supporting EThernet interface for node {}, dest tp {}", nodeId, destTp);
                         crossConnectFlag++;
                         // create OpenRoadm Xponder Client Interfaces
-                        createdEthInterfaces.add(this.openRoadmInterfaceFactory.createOpenRoadmEthInterface(
-                                nodeId, destTp));
+                        String openRoadmEthInterface = this.openRoadmInterfaceFactory.createOpenRoadmEthInterface(
+                                nodeId, destTp);
+                        createdEthInterfaces.add(openRoadmEthInterface);
+                        transactionHistory.add(new DeviceInterface(nodeId, openRoadmEthInterface));
                     }
                     if ((srcTp != null) && (srcTp.contains(StringConstants.TTP_TOKEN)
                             || srcTp.contains(StringConstants.PP_TOKEN))) {
                         LOG.info("Adding supporting OCH interface for node {}, src tp {}, spectrumInformation {}",
                                 nodeId, srcTp, spectrumInformation);
-                        createdOchInterfaces.addAll(this.openRoadmInterfaceFactory.createOpenRoadmOchInterfaces(
-                                nodeId, srcTp, spectrumInformation));
+                        List<String> openRoadmOchInterfaces = this.openRoadmInterfaceFactory
+                                .createOpenRoadmOchInterfaces(nodeId, srcTp, spectrumInformation);
+                        createdOchInterfaces.addAll(openRoadmOchInterfaces);
+                        transactionHistory.addInterfaces(nodeId, openRoadmOchInterfaces);
                     }
                     if ((destTp != null) && (destTp.contains(StringConstants.TTP_TOKEN)
                             || destTp.contains(StringConstants.PP_TOKEN))) {
                         LOG.info("Adding supporting OCH interface for node {}, dest tp {}, spectrumInformation {}",
                                 nodeId, destTp, spectrumInformation);
-                        createdOchInterfaces.addAll(this.openRoadmInterfaceFactory.createOpenRoadmOchInterfaces(
-                                nodeId, destTp, spectrumInformation));
+                        List<String> openRoadmOchInterfaces = this.openRoadmInterfaceFactory
+                                .createOpenRoadmOchInterfaces(nodeId, destTp, spectrumInformation);
+                        createdOchInterfaces.addAll(openRoadmOchInterfaces);
+                        transactionHistory.addInterfaces(nodeId, openRoadmOchInterfaces);
                     }
                     if (crossConnectFlag < 1) {
                         LOG.info("Creating cross connect between source {} and destination {} for node {}", srcTp,
@@ -261,7 +311,9 @@ public class DeviceRendererServiceImpl implements DeviceRendererService {
                                 this.crossConnect.postCrossConnect(nodeId, srcTp, destTp, spectrumInformation);
                         if (connectionNameOpt.isPresent()) {
                             nodesProvisioned.add(nodeId);
-                            createdConnections.add(connectionNameOpt.orElseThrow());
+                            String connectionName = connectionNameOpt.orElseThrow();
+                            createdConnections.add(connectionName);
+                            transactionHistory.add(new Connection(nodeId, connectionName, false));
                         } else {
                             processErrorMessage("Unable to post Roadm-connection for node " + nodeId, forkJoinPool,
                                     results);
@@ -558,6 +610,26 @@ public class DeviceRendererServiceImpl implements DeviceRendererService {
                 .build();
     }
 
+    @Override
+    public RendererRollbackOutput rendererRollback(History transactionHistory) {
+        LOG.info("Rolling back...");
+
+        Result rollbackResult = new FailedRollbackResult();
+        Subscriber deleteSubscriber = new DeleteSubscriber(rollbackResult);
+
+        transactionHistory.rollback(
+                new DeleteService(
+                        crossConnect,
+                        openRoadmInterfaces,
+                        deleteSubscriber
+                )
+        );
+
+        LOG.info("Rollback done!");
+
+        return rollbackResult.renderRollbackOutput();
+    }
+
     private boolean alarmSuppressionNodeRegistration(ServicePathInput input) {
         Map<org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.alarmsuppression.rev171102.service
                 .nodelist.nodelist.NodesKey,
index 328a254cad3c94c3af45aa755705c2d13611dfdb..fb29f90c854a1fb6a538ade8f9dfc957cb4e70e2 100644 (file)
@@ -36,10 +36,14 @@ import org.opendaylight.transportpce.renderer.ServicePathInputData;
 import org.opendaylight.transportpce.renderer.provisiondevice.servicepath.ServicePathDirection;
 import org.opendaylight.transportpce.renderer.provisiondevice.tasks.DeviceRenderingRollbackTask;
 import org.opendaylight.transportpce.renderer.provisiondevice.tasks.DeviceRenderingTask;
+import org.opendaylight.transportpce.renderer.provisiondevice.tasks.NetworkDeviceRenderingRollbackTask;
 import org.opendaylight.transportpce.renderer.provisiondevice.tasks.OlmPowerSetupRollbackTask;
 import org.opendaylight.transportpce.renderer.provisiondevice.tasks.OlmPowerSetupTask;
 import org.opendaylight.transportpce.renderer.provisiondevice.tasks.OtnDeviceRenderingTask;
 import org.opendaylight.transportpce.renderer.provisiondevice.tasks.RollbackProcessor;
+import org.opendaylight.transportpce.renderer.provisiondevice.tasks.RollbackResultMessage;
+import org.opendaylight.transportpce.renderer.provisiondevice.transaction.history.History;
+import org.opendaylight.transportpce.renderer.provisiondevice.transaction.history.TransactionHistory;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.device.renderer.rev211004.Action;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.device.renderer.rev211004.OtnServicePathInput;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.networkutils.rev220630.OtnLinkType;
@@ -370,9 +374,12 @@ public class RendererServiceOperationsImpl implements RendererServiceOperations
             servicePathDataAtoZ.getServicePathInput().getServiceName(),
             RpcStatusEx.Pending,
             RENDERING_DEVICES_A_Z_MSG);
+
+        History transactionHistory = new TransactionHistory();
         ListenableFuture<DeviceRenderingResult> atozrenderingFuture =
             this.executor.submit(
-                new DeviceRenderingTask(this.deviceRenderer, servicePathDataAtoZ, ServicePathDirection.A_TO_Z));
+                new DeviceRenderingTask(this.deviceRenderer, servicePathDataAtoZ, ServicePathDirection.A_TO_Z,
+                        transactionHistory));
 
         LOG.info(RENDERING_DEVICES_Z_A_MSG);
         sendNotifications(
@@ -382,7 +389,8 @@ public class RendererServiceOperationsImpl implements RendererServiceOperations
             RENDERING_DEVICES_Z_A_MSG);
         ListenableFuture<DeviceRenderingResult> ztoarenderingFuture =
             this.executor.submit(
-                new DeviceRenderingTask(this.deviceRenderer, servicePathDataZtoA, ServicePathDirection.Z_TO_A));
+                new DeviceRenderingTask(this.deviceRenderer, servicePathDataZtoA, ServicePathDirection.Z_TO_A,
+                        transactionHistory));
 
         ListenableFuture<List<DeviceRenderingResult>> renderingCombinedFuture =
             Futures.allAsList(atozrenderingFuture, ztoarenderingFuture);
@@ -403,16 +411,15 @@ public class RendererServiceOperationsImpl implements RendererServiceOperations
         }
 
         rollbackProcessor.addTask(
-            new DeviceRenderingRollbackTask(
-                "AtoZDeviceTask",
-                ! renderingResults.get(0).isSuccess(),
-                renderingResults.get(0).getRenderedNodeInterfaces(),
-                this.deviceRenderer));
-        rollbackProcessor.addTask(
-                new DeviceRenderingRollbackTask("ZtoADeviceTask",
-                ! renderingResults.get(1).isSuccess(),
-                renderingResults.get(1).getRenderedNodeInterfaces(),
-                this.deviceRenderer));
+            new NetworkDeviceRenderingRollbackTask(
+                "RollbackTransactionHistoryTask",
+                transactionHistory,
+                ! (renderingResults.get(0).isSuccess() && renderingResults.get(1).isSuccess()),
+                deviceRenderer,
+                new RollbackResultMessage()
+            )
+        );
+
         return renderingResults;
     }
 
index 6a30f950d7f00efc5d24eb19c638e32a57b364b5..4b2d7fbed1364662aacf5d3b0a11d54ef4f3adf7 100644 (file)
@@ -14,6 +14,7 @@ import org.opendaylight.transportpce.renderer.ServicePathInputData;
 import org.opendaylight.transportpce.renderer.provisiondevice.DeviceRendererService;
 import org.opendaylight.transportpce.renderer.provisiondevice.DeviceRenderingResult;
 import org.opendaylight.transportpce.renderer.provisiondevice.servicepath.ServicePathDirection;
+import org.opendaylight.transportpce.renderer.provisiondevice.transaction.history.History;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.device.renderer.rev211004.ServicePathOutput;
 import org.opendaylight.yang.gen.v1.http.org.transportpce.common.types.rev220926.optical.renderer.nodes.Nodes;
 import org.slf4j.Logger;
@@ -26,12 +27,14 @@ public class DeviceRenderingTask implements Callable<DeviceRenderingResult> {
     private final DeviceRendererService deviceRenderer;
     private final ServicePathInputData servicePathInputData;
     private final ServicePathDirection direction;
+    private final History transactionHistory;
 
     public DeviceRenderingTask(DeviceRendererService deviceRenderer, ServicePathInputData servicePathInputData,
-            ServicePathDirection direction) {
+            ServicePathDirection direction, History transactionHistory) {
         this.deviceRenderer = deviceRenderer;
         this.servicePathInputData = servicePathInputData;
         this.direction = direction;
+        this.transactionHistory = transactionHistory;
     }
 
     @Override
@@ -43,7 +46,7 @@ public class DeviceRenderingTask implements Callable<DeviceRenderingResult> {
             case Create:
                 operation = "setup";
                 output = this.deviceRenderer.setupServicePath(this.servicePathInputData.getServicePathInput(),
-                    this.direction);
+                    this.direction, transactionHistory);
                 olmList = this.servicePathInputData.getNodeLists().getOlmNodeList();
                 break;
             case Delete:
diff --git a/renderer/src/main/java/org/opendaylight/transportpce/renderer/provisiondevice/tasks/NetworkDeviceRenderingRollbackTask.java b/renderer/src/main/java/org/opendaylight/transportpce/renderer/provisiondevice/tasks/NetworkDeviceRenderingRollbackTask.java
new file mode 100644 (file)
index 0000000..4c1faa1
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright © 2024 Smartoptics and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.transportpce.renderer.provisiondevice.tasks;
+
+import org.opendaylight.transportpce.renderer.provisiondevice.DeviceRendererService;
+import org.opendaylight.transportpce.renderer.provisiondevice.transaction.history.History;
+import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.device.renderer.rev211004.RendererRollbackOutput;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class NetworkDeviceRenderingRollbackTask extends RollbackTask {
+
+    private final History transactionHistory;
+
+    private final boolean isRollbackNecessary;
+
+    private final DeviceRendererService deviceRendererService;
+
+    private final ResultMessage message;
+
+    private static final Logger LOG = LoggerFactory.getLogger(NetworkDeviceRenderingRollbackTask.class);
+
+    public NetworkDeviceRenderingRollbackTask(String id, History transactionHistory,
+                                              boolean isRollbackNecessary,
+                                              DeviceRendererService deviceRendererService, ResultMessage message) {
+        super(id);
+        this.transactionHistory = transactionHistory;
+        this.isRollbackNecessary = isRollbackNecessary;
+        this.deviceRendererService = deviceRendererService;
+        this.message = message;
+    }
+
+    @Override
+    public boolean isRollbackNecessary() {
+        return isRollbackNecessary;
+    }
+
+    @Override
+    public Void call() throws Exception {
+
+        RendererRollbackOutput rollbackOutput = deviceRendererService.rendererRollback(transactionHistory);
+
+        if (! rollbackOutput.getSuccess()) {
+            LOG.warn("Device rendering rollback of {} was not successful! Failed rollback on {}.", this.getId(),
+                    message.createErrorMessage(rollbackOutput.nonnullFailedToRollback().values()));
+        } else {
+            LOG.info("Device rollback of {} successful.", this.getId());
+        }
+
+        return null;
+    }
+}
diff --git a/renderer/src/main/java/org/opendaylight/transportpce/renderer/provisiondevice/tasks/ResultMessage.java b/renderer/src/main/java/org/opendaylight/transportpce/renderer/provisiondevice/tasks/ResultMessage.java
new file mode 100644 (file)
index 0000000..d1540ca
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright © 2024 Smartoptics and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.transportpce.renderer.provisiondevice.tasks;
+
+import java.util.Collection;
+import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.device.renderer.rev211004.renderer.rollback.output.FailedToRollback;
+
+public interface ResultMessage {
+
+    /**
+     * Build an error message for a failed rollback.
+     */
+    String createErrorMessage(Collection<FailedToRollback> failedRollbacks);
+
+}
\ No newline at end of file
diff --git a/renderer/src/main/java/org/opendaylight/transportpce/renderer/provisiondevice/tasks/RollbackResultMessage.java b/renderer/src/main/java/org/opendaylight/transportpce/renderer/provisiondevice/tasks/RollbackResultMessage.java
new file mode 100644 (file)
index 0000000..7fae4c9
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright © 2024 Smartoptics and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.transportpce.renderer.provisiondevice.tasks;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.device.renderer.rev211004.renderer.rollback.output.FailedToRollback;
+
+public class RollbackResultMessage implements ResultMessage {
+
+    @Override
+    public String createErrorMessage(Collection<FailedToRollback> failedRollbacks) {
+        List<String> failedRollbackNodes = new ArrayList<>();
+
+        failedRollbacks.forEach(failedRollback -> {
+            var intf = failedRollback.getInterface();
+
+            failedRollbackNodes.add(
+                failedRollback.getNodeId()
+                    + ": "
+                    + intf == null ? "" : String.join(", ", intf)
+            );
+        });
+
+        return String.join(System.lineSeparator(), failedRollbackNodes);
+    }
+
+}
index 63e347868f6cf6dd6ae3fb91136a1007e0772fbc..fe1973618c1953c37a4386b1d65ac46f92c51d9b 100644 (file)
@@ -101,7 +101,7 @@ public class RendererServiceOperationsImplTest extends AbstractTest {
             .buildServiceImplementationRequestInputTerminationPointResource(StringConstants.TTP_TOKEN);
         ServicePathOutputBuilder mockOutputBuilder = new ServicePathOutputBuilder().setResult("success")
             .setSuccess(true);
-        doReturn(mockOutputBuilder.build()).when(this.deviceRenderer).setupServicePath(any(), any());
+        doReturn(mockOutputBuilder.build()).when(this.deviceRenderer).setupServicePath(any(), any(), any());
         ServiceImplementationRequestOutput result =
                 this.rendererServiceOperations.serviceImplementation(input, false).get();
         assertEquals(ResponseCodes.RESPONSE_OK, result.getConfigurationResponseCommon().getResponseCode());
@@ -113,7 +113,7 @@ public class RendererServiceOperationsImplTest extends AbstractTest {
             .buildServiceImplementationRequestInputTerminationPointResource(StringConstants.TTP_TOKEN);
         ServicePathOutputBuilder mockOutputBuilder = new ServicePathOutputBuilder().setResult("success")
             .setSuccess(true);
-        doReturn(mockOutputBuilder.build()).when(this.deviceRenderer).setupServicePath(any(), any());
+        doReturn(mockOutputBuilder.build()).when(this.deviceRenderer).setupServicePath(any(), any(), any());
         doReturn(RpcResultBuilder.failed().buildFuture()).when(this.olmService).servicePowerSetup(any());
         ServiceImplementationRequestOutput result =
                 this.rendererServiceOperations.serviceImplementation(input, false).get();
@@ -126,7 +126,7 @@ public class RendererServiceOperationsImplTest extends AbstractTest {
             .buildServiceImplementationRequestInputTerminationPointResource(StringConstants.PP_TOKEN);
         ServicePathOutputBuilder mockOutputBuilder = new ServicePathOutputBuilder().setResult("success")
             .setSuccess(true);
-        doReturn(mockOutputBuilder.build()).when(this.deviceRenderer).setupServicePath(any(), any());
+        doReturn(mockOutputBuilder.build()).when(this.deviceRenderer).setupServicePath(any(), any(), any());
         ServiceImplementationRequestOutput result = this.rendererServiceOperations.serviceImplementation(input,
                 false).get();
         assertEquals(ResponseCodes.RESPONSE_OK, result.getConfigurationResponseCommon().getResponseCode());
@@ -138,7 +138,7 @@ public class RendererServiceOperationsImplTest extends AbstractTest {
             .buildServiceImplementationRequestInputTerminationPointResource(StringConstants.NETWORK_TOKEN);
         ServicePathOutputBuilder mockOutputBuilder = new ServicePathOutputBuilder().setResult("success")
             .setSuccess(true);
-        doReturn(mockOutputBuilder.build()).when(this.deviceRenderer).setupServicePath(any(), any());
+        doReturn(mockOutputBuilder.build()).when(this.deviceRenderer).setupServicePath(any(), any(), any());
         ServiceImplementationRequestOutput result = this.rendererServiceOperations.serviceImplementation(input,
                 false).get();
         assertEquals(ResponseCodes.RESPONSE_OK, result.getConfigurationResponseCommon().getResponseCode());
@@ -150,7 +150,7 @@ public class RendererServiceOperationsImplTest extends AbstractTest {
             .buildServiceImplementationRequestInputTerminationPointResource(StringConstants.CLIENT_TOKEN);
         ServicePathOutputBuilder mockOutputBuilder = new ServicePathOutputBuilder().setResult("success")
             .setSuccess(true);
-        doReturn(mockOutputBuilder.build()).when(this.deviceRenderer).setupServicePath(any(), any());
+        doReturn(mockOutputBuilder.build()).when(this.deviceRenderer).setupServicePath(any(), any(), any());
         ServiceImplementationRequestOutput result = this.rendererServiceOperations.serviceImplementation(input,
                 false).get();
         assertEquals(ResponseCodes.RESPONSE_OK, result.getConfigurationResponseCommon().getResponseCode());
@@ -167,7 +167,7 @@ public class RendererServiceOperationsImplTest extends AbstractTest {
 
         ServicePathOutputBuilder mockOutputBuilder = new ServicePathOutputBuilder().setResult("failed")
             .setSuccess(false);
-        doReturn(mockOutputBuilder.build()).when(this.deviceRenderer).setupServicePath(any(), any());
+        doReturn(mockOutputBuilder.build()).when(this.deviceRenderer).setupServicePath(any(), any(), any());
 
         for (String tpToken : interfaceTokens) {
             ServiceImplementationRequestInput input = ServiceDataUtils
@@ -247,7 +247,7 @@ public class RendererServiceOperationsImplTest extends AbstractTest {
         when(this.olmService.getPm(eq(getPmInputA))).thenReturn(RpcResultBuilder.success(getPmOutput).buildFuture());
         ServicePathOutputBuilder mockOutputBuilder = new ServicePathOutputBuilder().setResult("success")
             .setSuccess(true);
-        doReturn(mockOutputBuilder.build()).when(this.deviceRenderer).setupServicePath(any(), any());
+        doReturn(mockOutputBuilder.build()).when(this.deviceRenderer).setupServicePath(any(), any(), any());
         ServiceImplementationRequestInput input = ServiceDataUtils
             .buildServiceImplementationRequestInputTerminationPointResource(StringConstants.NETWORK_TOKEN);
         ServiceImplementationRequestOutput result = this.rendererServiceOperations.serviceImplementation(input,
@@ -263,7 +263,7 @@ public class RendererServiceOperationsImplTest extends AbstractTest {
         when(this.olmService.getPm(any())).thenReturn(RpcResultBuilder.success(getPmOutput1).buildFuture());
         ServicePathOutputBuilder mockOutputBuilder = new ServicePathOutputBuilder().setResult("success")
             .setSuccess(true);
-        doReturn(mockOutputBuilder.build()).when(this.deviceRenderer).setupServicePath(any(), any());
+        doReturn(mockOutputBuilder.build()).when(this.deviceRenderer).setupServicePath(any(), any(), any());
         ServiceImplementationRequestOutput result = this.rendererServiceOperations.serviceImplementation(input,
                 false).get();
         assertEquals(ResponseCodes.RESPONSE_OK, result.getConfigurationResponseCommon().getResponseCode());
@@ -277,7 +277,7 @@ public class RendererServiceOperationsImplTest extends AbstractTest {
         when(this.olmService.getPm(any())).thenReturn(RpcResultBuilder.success(getPmOutput).buildFuture());
         ServicePathOutputBuilder mockOutputBuilder = new ServicePathOutputBuilder().setResult("success")
             .setSuccess(true);
-        doReturn(mockOutputBuilder.build()).when(this.deviceRenderer).setupServicePath(any(), any());
+        doReturn(mockOutputBuilder.build()).when(this.deviceRenderer).setupServicePath(any(), any(), any());
         ServiceImplementationRequestOutput result = this.rendererServiceOperations.serviceImplementation(input,
                 false).get();
         assertEquals(ResponseCodes.RESPONSE_OK, result.getConfigurationResponseCommon().getResponseCode());
@@ -298,7 +298,7 @@ public class RendererServiceOperationsImplTest extends AbstractTest {
         doReturn(RpcResultBuilder.success(getPmOutput).buildFuture()).when(this.olmService).getPm(any());
         ServicePathOutputBuilder mockOutputBuilder = new ServicePathOutputBuilder().setResult("success")
             .setSuccess(true);
-        doReturn(mockOutputBuilder.build()).when(this.deviceRenderer).setupServicePath(any(), any());
+        doReturn(mockOutputBuilder.build()).when(this.deviceRenderer).setupServicePath(any(), any(), any());
         ServiceImplementationRequestInput input = ServiceDataUtils
             .buildServiceImplementationRequestInputTerminationPointResource(StringConstants.NETWORK_TOKEN);
         ServiceImplementationRequestOutput result = this.rendererServiceOperations.serviceImplementation(input,