Activation status handling mechanism for ForwardingConstruct provisioning 77/42977/2
authorKrzysztof Bijakowski <krzysztof.bijakowski@amartus.com>
Thu, 28 Jul 2016 10:18:43 +0000 (12:18 +0200)
committerKrzysztof Bijakowski <krzysztof.bijakowski@amartus.com>
Wed, 3 Aug 2016 09:21:34 +0000 (11:21 +0200)
Change-Id: Ibbc85900c6205e2f09a8a2608afe7abb2b322bf4
Signed-off-by: Krzysztof Bijakowski <krzysztof.bijakowski@amartus.com>
cisco-xr-driver/src/test/java/org/opendaylight/unimgr/mef/nrp/cisco/xr/l2vpn/activator/L2vpnBridgeActivatorTest.java
impl/pom.xml
impl/src/main/java/org/opendaylight/unimgr/impl/ForwardingConstructActivationStateTracker.java [new file with mode: 0644]
impl/src/main/java/org/opendaylight/unimgr/impl/ForwardingConstructActivatorService.java
impl/src/main/java/org/opendaylight/unimgr/impl/ForwardingConstructChangeListener.java
impl/src/main/java/org/opendaylight/unimgr/mef/nrp/impl/ActivationTransaction.java
impl/src/test/java/org/opendaylight/unimgr/impl/FcRouteActivatorServiceTest.java
impl/src/test/java/org/opendaylight/unimgr/impl/ForwardingConstructActivationStateTrackerTest.java [new file with mode: 0644]
impl/src/test/java/org/opendaylight/unimgr/impl/ForwardingConstructTestUtils.java [new file with mode: 0644]
presto-api/src/main/yang/mef-unimgr-ext.yang [new file with mode: 0644]

index dfb556d75d2f237dc3799681ffee9f69a155c3d3..5a5e01e574cab290b9d62093da69fe47cb8468cf 100644 (file)
@@ -34,6 +34,8 @@ import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
 
 /**
@@ -116,10 +118,16 @@ public class L2vpnBridgeActivatorTest extends AbstractDataBrokerTest{
            P2pXconnect p2pXconnect = xconnectGroup.getP2pXconnects().getP2pXconnect().get(0);
            L2vpnActivatorTestUtils.checkP2pXconnect(p2pXconnect,innerName);
 
-           AttachmentCircuit attachmentCircuit = p2pXconnect.getAttachmentCircuits().getAttachmentCircuit().get(0);
-           L2vpnActivatorTestUtils.checkAttachmentCircuit(attachmentCircuit,portNo2);
-           attachmentCircuit = p2pXconnect.getAttachmentCircuits().getAttachmentCircuit().get(1);
-           L2vpnActivatorTestUtils.checkAttachmentCircuit(attachmentCircuit,portNo1);
+           List<AttachmentCircuit> attachmentCircuits = p2pXconnect.getAttachmentCircuits().getAttachmentCircuit();
+           assertNotNull(attachmentCircuits);
+           assertEquals(2, attachmentCircuits.size());
+
+           attachmentCircuits.sort(
+                   (AttachmentCircuit ac1, AttachmentCircuit ac2)
+                           -> ac1.getName().getValue().compareTo(ac2.getName().getValue()));
+
+           L2vpnActivatorTestUtils.checkAttachmentCircuit(attachmentCircuits.get(0), portNo1);
+           L2vpnActivatorTestUtils.checkAttachmentCircuit(attachmentCircuits.get(1), portNo2);
        } else {
            fail("L2vpn was not found.");
        }
index 4998f0915f9c98551ff693ce5377769f96719132..9c3eee09ede4d5beec34756a2a5e4b00a95f2ed3 100644 (file)
@@ -76,6 +76,19 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL
             <version>1.1.0-SNAPSHOT</version>
         </dependency>
 
+        <!-- dependencies to use AbstractDataBrokerTest -->
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>sal-binding-broker-impl</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>sal-binding-broker-impl</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+
         <!-- Testing Dependencies -->
         <dependency>
             <groupId>junit</groupId>
diff --git a/impl/src/main/java/org/opendaylight/unimgr/impl/ForwardingConstructActivationStateTracker.java b/impl/src/main/java/org/opendaylight/unimgr/impl/ForwardingConstructActivationStateTracker.java
new file mode 100644 (file)
index 0000000..aba2566
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2016 Cisco Systems Inc 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.unimgr.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.yang.gen.v1.urn.mef.unimgr.ext.rev160725.ActivationStatus;
+import org.opendaylight.yang.gen.v1.urn.mef.unimgr.ext.rev160725.ForwardingConstruct1;
+import org.opendaylight.yang.gen.v1.urn.mef.unimgr.ext.rev160725.ForwardingConstruct1Builder;
+import org.opendaylight.yang.gen.v1.urn.mef.unimgr.ext.rev160725.forwarding.constructs.forwarding.construct.UnimgrAttrsBuilder;
+import org.opendaylight.yang.gen.v1.urn.onf.core.network.module.rev160630.forwarding.constructs.ForwardingConstruct;
+import org.opendaylight.yang.gen.v1.urn.onf.core.network.module.rev160630.forwarding.constructs.ForwardingConstructBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Forwarding construct activation state tracking support.
+ *
+ * @author krzysztof.bijakowski@amartus.com
+ */
+public class ForwardingConstructActivationStateTracker {
+    private static final Logger LOG = LoggerFactory.getLogger(ForwardingConstructActivationStateTracker.class);
+
+    private DataBroker dataBroker;
+
+    private InstanceIdentifier<ForwardingConstruct> fcIid;
+
+    public ForwardingConstructActivationStateTracker(DataBroker dataBroker, InstanceIdentifier<ForwardingConstruct> fcIid) {
+        this.dataBroker = dataBroker;
+        this.fcIid = fcIid;
+    }
+
+    public boolean isActivatable() {
+        ReadOnlyTransaction tx = dataBroker.newReadOnlyTransaction();
+
+        try {
+            CheckedFuture<Optional<ForwardingConstruct>, ReadFailedException> result = tx.read(LogicalDatastoreType.OPERATIONAL, fcIid);
+            Optional<ForwardingConstruct> fcOptional = result.checkedGet();
+            return !fcOptional.isPresent();
+        } catch (ReadFailedException e) {
+            LOG.warn("Error during forwarding construct activation state checking", e);
+        }
+
+        return false;
+    }
+
+    public boolean isDeactivatable() {
+        return !isActivatable();
+    }
+
+    public void activated(ForwardingConstruct forwardingConstruct) {
+        writeActivationData(forwardingConstruct, ActivationStatus.ACTIVE);
+    }
+
+    public void activationFailed(ForwardingConstruct forwardingConstruct) {
+        writeActivationData(forwardingConstruct, ActivationStatus.FAILED);
+    }
+
+    public void deactivated() {
+        WriteTransaction transaction = dataBroker.newWriteOnlyTransaction();
+        transaction.delete(LogicalDatastoreType.OPERATIONAL, fcIid);
+
+        try {
+            transaction.submit().checkedGet();
+            LOG.debug("Forwarding construct activation state information deleted successfully");
+        } catch (TransactionCommitFailedException e) {
+            LOG.warn("Error during forwarding construct activation state information deletion", e);
+        }
+    }
+
+    public void deactivationFailed() {
+        //TODO consider how this logic should work
+    }
+
+    @Override
+    public ForwardingConstructActivationStateTracker clone() throws CloneNotSupportedException {
+        return (ForwardingConstructActivationStateTracker) super.clone();
+    }
+
+    private void writeActivationData(ForwardingConstruct forwardingConstruct, ActivationStatus activationStatus) {
+        WriteTransaction transaction = dataBroker.newWriteOnlyTransaction();
+
+        ForwardingConstruct1 augmentation = new ForwardingConstruct1Builder()
+            .setUnimgrAttrs(new UnimgrAttrsBuilder().setStatus(activationStatus).build())
+            .build();
+
+        ForwardingConstruct update = new ForwardingConstructBuilder(forwardingConstruct)
+            .addAugmentation(ForwardingConstruct1.class, augmentation)
+            .build();
+
+        transaction.merge(LogicalDatastoreType.OPERATIONAL, fcIid, update);
+
+        try {
+            transaction.submit().checkedGet();
+            LOG.debug("Forwarding construct activation state information wrote successfully");
+        } catch (TransactionCommitFailedException e) {
+            LOG.warn("Error during writing forwarding construct activation state information", e);
+        }
+    }
+}
index 43f853a3d2108cabc8fe017870fc3f91f6243371..274a8661581959d322deeb1dec782661dd76895a 100644 (file)
@@ -25,8 +25,11 @@ import org.opendaylight.yang.gen.v1.urn.onf.core.network.module.rev160630.g_forw
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static org.opendaylight.unimgr.mef.nrp.impl.ActivationTransaction.Result;
+
 /**
  * @author bartosz.michalik@amartus.com
+ * @author krzysztof.bijakowski@amartus.com [modifications]
  */
 public class ForwardingConstructActivatorService {
     private static final Logger LOG = LoggerFactory.getLogger(ForwardingConstructActivatorService.class);
@@ -42,12 +45,22 @@ public class ForwardingConstructActivatorService {
      * Activate a MEF ForwardingConstruct.
      * @param forwardingConstruct the new route to activate
      */
-    public void activate(@Nonnull ForwardingConstruct forwardingConstruct) {
-        Optional<ActivationTransaction> tx = prepareTransaction(forwardingConstruct);
-        if (tx.isPresent()) {
-            tx.get().activate();
-        } else {
-            LOG.warn("No transaction for this activation request {}", forwardingConstruct);
+    public void activate(@Nonnull ForwardingConstruct forwardingConstruct, @Nonnull ForwardingConstructActivationStateTracker stateTracker) {
+        if(stateTracker.isActivatable()) {
+            Optional<ActivationTransaction> tx = prepareTransaction(forwardingConstruct);
+            if (tx.isPresent()) {
+                Result result = tx.get().activate();
+
+                if(result.isSuccessful()) {
+                    stateTracker.activated(forwardingConstruct);
+                    LOG.info("Forwarding construct activated successfully, request = {} ", forwardingConstruct);
+                } else {
+                    stateTracker.activationFailed(forwardingConstruct);
+                    LOG.warn("Forwarding construct activation failed, reason = {}, request = {}", result.getMessage(), forwardingConstruct);
+                }
+            } else {
+                LOG.warn("No transaction for this activation request {}", forwardingConstruct);
+            }
         }
     }
 
@@ -55,12 +68,22 @@ public class ForwardingConstructActivatorService {
      * Deactivate a MEF ForwardingConstruct.
      * @param forwardingConstruct the existing route to deactivate
      */
-    public void deactivate(@Nonnull ForwardingConstruct forwardingConstruct) {
-        Optional<ActivationTransaction> tx = prepareTransaction(forwardingConstruct);
-        if (tx.isPresent()) {
-            tx.get().deactivate();
-        } else {
-            LOG.warn("No transaction for this activation request {}", forwardingConstruct);
+    public void deactivate(@Nonnull ForwardingConstruct forwardingConstruct, @Nonnull ForwardingConstructActivationStateTracker stateTracker) {
+        if(stateTracker.isDeactivatable()) {
+            Optional<ActivationTransaction> tx = prepareTransaction(forwardingConstruct);
+            if (tx.isPresent()) {
+                Result result = tx.get().deactivate();
+
+                if(result.isSuccessful()) {
+                    stateTracker.deactivated();
+                    LOG.info("Forwarding construct deactivated successfully, request = {}", forwardingConstruct);
+                } else {
+                    stateTracker.deactivationFailed();
+                    LOG.warn("Forwarding construct deactivation failed, reason = {}, request = {}", result.getMessage(), forwardingConstruct);
+                }
+            } else {
+                LOG.warn("No transaction for this deactivation request {}", forwardingConstruct);
+            }
         }
     }
 
index 729d847ffc871df3836349a675628a3c12a92cdd..44af5d31198feca4759506a45add2ed6f3fba873 100644 (file)
@@ -9,11 +9,7 @@ package org.opendaylight.unimgr.impl;
 
 import java.util.Collection;
 
-import org.opendaylight.controller.md.sal.binding.api.DataBroker;
-import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
-import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
-import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
-import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.controller.md.sal.binding.api.*;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.unimgr.mef.nrp.api.ActivationDriverRepoService;
 import org.opendaylight.yang.gen.v1.urn.onf.core.network.module.rev160630.ForwardingConstructs;
@@ -29,16 +25,21 @@ import org.slf4j.LoggerFactory;
  */
 public class ForwardingConstructChangeListener implements DataTreeChangeListener<ForwardingConstruct>, AutoCloseable {
     private static final Logger LOG = LoggerFactory.getLogger(ForwardingConstructChangeListener.class);
+
     private final ListenerRegistration<ForwardingConstructChangeListener> listener;
+
     private final ForwardingConstructActivatorService routeActivator;
 
     private final ActivationDriverRepoService activationRepoService;
 
+    private final DataBroker dataBroker;
+
     public ForwardingConstructChangeListener(DataBroker dataBroker, ActivationDriverRepoService activationRepoService) {
+        this.dataBroker  = dataBroker;
         this.activationRepoService = activationRepoService;
         routeActivator = new ForwardingConstructActivatorService(activationRepoService);
 
-        final InstanceIdentifier<ForwardingConstruct> fwPath = getFwConstructsPath();
+        final InstanceIdentifier<ForwardingConstruct> fwPath = getFcPath();
         final DataTreeIdentifier<ForwardingConstruct> dataTreeIid =
                 new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, fwPath);
         listener = dataBroker.registerDataTreeChangeListener(dataTreeIid, this);
@@ -59,6 +60,7 @@ public class ForwardingConstructChangeListener implements DataTreeChangeListener
         }
         for (final DataTreeModification<ForwardingConstruct> change : collection) {
             final DataObjectModification<ForwardingConstruct> root = change.getRootNode();
+
             switch (root.getModificationType()) {
                 case SUBTREE_MODIFIED:
                     update(change);
@@ -86,14 +88,19 @@ public class ForwardingConstructChangeListener implements DataTreeChangeListener
     protected void add(DataTreeModification<ForwardingConstruct> newDataObject) {
         //TODO: Refine the logged addition
         LOG.debug("FcRoute add event received {}", newDataObject);
-        routeActivator.activate(newDataObject.getRootNode().getDataAfter());
-
+        routeActivator.activate(
+            getFcForActivation(newDataObject),
+            getFcActivationStateTracker(newDataObject)
+        );
     }
 
     protected void remove(DataTreeModification<ForwardingConstruct> removedDataObject) {
         //TODO: Refine the logged removal
         LOG.debug("FcRoute remove event received {}", removedDataObject);
-        routeActivator.deactivate(removedDataObject.getRootNode().getDataBefore());
+        routeActivator.deactivate(
+            getFcForDeactivation(removedDataObject),
+            getFcActivationStateTracker(removedDataObject)
+        );
 
     }
 
@@ -102,8 +109,9 @@ public class ForwardingConstructChangeListener implements DataTreeChangeListener
         LOG.debug("FcRoute update event received {}", modifiedDataObject);
 
         //TODO for the moment transactional nature of this action is ignored :P
-        routeActivator.deactivate(modifiedDataObject.getRootNode().getDataBefore());
-        routeActivator.activate(modifiedDataObject.getRootNode().getDataAfter());
+        ForwardingConstructActivationStateTracker stateTracker = getFcActivationStateTracker(modifiedDataObject);
+        routeActivator.deactivate(getFcForDeactivation(modifiedDataObject), stateTracker);
+        routeActivator.activate(getFcForActivation(modifiedDataObject), stateTracker);
     }
 
     @Override
@@ -111,9 +119,26 @@ public class ForwardingConstructChangeListener implements DataTreeChangeListener
         listener.close();
     }
 
-
-    private InstanceIdentifier<ForwardingConstruct> getFwConstructsPath() {
+    private InstanceIdentifier<ForwardingConstruct> getFcPath() {
         return InstanceIdentifier
                 .builder(ForwardingConstructs.class).child(ForwardingConstruct.class).build();
     }
+
+    private InstanceIdentifier<ForwardingConstruct> getFcIid(DataTreeModification<ForwardingConstruct> fcModification) {
+        return fcModification.getRootPath().getRootIdentifier();
+    }
+
+    private ForwardingConstruct getFcForActivation(DataTreeModification<ForwardingConstruct> fcModification) {
+        return fcModification.getRootNode().getDataAfter();
+    }
+
+    private ForwardingConstruct getFcForDeactivation(DataTreeModification<ForwardingConstruct> fcModification) {
+        return fcModification.getRootNode().getDataBefore();
+    }
+
+
+    private ForwardingConstructActivationStateTracker getFcActivationStateTracker(
+            DataTreeModification<ForwardingConstruct> fcModification) {
+        return new ForwardingConstructActivationStateTracker(dataBroker, getFcIid(fcModification));
+    }
 }
index 1d0c6af471f1732c030cacd22d716969418a7a93..9cef7c021d53d56d404d644cc6ed7c8d21f51136 100644 (file)
@@ -10,6 +10,7 @@ package org.opendaylight.unimgr.mef.nrp.impl;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
 
 import org.opendaylight.unimgr.mef.nrp.api.ActivationDriver;
 import org.slf4j.Logger;
@@ -19,11 +20,12 @@ import org.slf4j.LoggerFactory;
  * Runs activation over multiple @ drivers.
  *
  * @author bartosz.michalik@amartus.com
+ * @author krzysztof.bijakowski@amartus.com [modifications]
  */
 public class ActivationTransaction {
     private static final Logger LOG = LoggerFactory.getLogger(ActivationTransaction.class);
-    private List<ActivationDriver> drivers = new ArrayList<>();
 
+    private List<ActivationDriver> drivers = new ArrayList<>();
 
     public void addDriver(ActivationDriver driver) {
         drivers.add(driver);
@@ -32,7 +34,7 @@ public class ActivationTransaction {
     /**
      * Activate the contents of this transaction.
      */
-    public void activate() {
+    public Result activate() {
         sortDrivers();
         try {
             for (ActivationDriver d: drivers) {
@@ -40,17 +42,21 @@ public class ActivationTransaction {
             }
             commit();
             LOG.info("Activate transaction successful");
+
+            return Result.success();
         } catch (Exception e) {
             //XXX add transaction identification ???
             LOG.warn("Rolling back activate transaction ", e);
             rollback();
+
+            return Result.fail(e.getMessage(), e);
         }
     }
 
     /**
      * Deactivate the contents of this transaction.
      */
-    public void deactivate() {
+    public Result deactivate() {
         sortDrivers();
         try {
             for (ActivationDriver d: drivers) {
@@ -58,10 +64,14 @@ public class ActivationTransaction {
             }
             LOG.info("Deactivate transaction successful");
             commit();
+
+            return Result.success();
         } catch (Exception e) {
             //XXX add transaction identification ???
             LOG.warn("Rolling back deactivate transaction ", e);
             rollback();
+
+            return Result.fail(e.getMessage(), e);
         }
     }
 
@@ -77,4 +87,38 @@ public class ActivationTransaction {
         drivers.sort((driverA, driverB) -> driverA.priority() - driverB.priority());
     }
 
+    public static class Result {
+        private boolean successful;
+
+        private Optional<String> message;
+
+        private Optional<Throwable> cause;
+
+        private Result(boolean successful, Optional<String> message, Optional<Throwable> cause) {
+            this.successful = successful;
+            this.message = message;
+            this.cause = cause;
+        }
+
+        public Optional<Throwable> getCause() {
+            return cause;
+        }
+
+        public boolean isSuccessful() {
+            return successful;
+        }
+
+        public Optional<String> getMessage() {
+            return message;
+        }
+
+        public static Result success(){
+            return new Result(true, Optional.empty(), Optional.empty());
+        }
+
+        public static Result fail(String message, Throwable cause){
+            return new Result(false, Optional.of(message), Optional.of(cause));
+        }
+    }
+
 }
index 360ace1714e8aba5bbaaf77ff9230a53ea734bed..4df47c33d75b90f5bbb93d5af1b35cacf716fc78 100644 (file)
@@ -7,29 +7,25 @@
  */
 package org.opendaylight.unimgr.impl;
 
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.function.Consumer;
-
+import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mockito;
 import org.opendaylight.unimgr.mef.nrp.api.ActivationDriver;
 import org.opendaylight.unimgr.mef.nrp.api.ActivationDriverBuilder;
 import org.opendaylight.unimgr.mef.nrp.impl.ActivationDriverRepoServiceImpl;
 import org.opendaylight.unimgr.utils.ActivationDriverMocks;
 import org.opendaylight.yang.gen.v1.urn.onf.core.network.module.rev160630.forwarding.constructs.ForwardingConstruct;
-import org.opendaylight.yang.gen.v1.urn.onf.core.network.module.rev160630.forwarding.constructs.ForwardingConstructBuilder;
 import org.opendaylight.yang.gen.v1.urn.onf.core.network.module.rev160630.g_forwardingconstruct.FcPort;
-import org.opendaylight.yang.gen.v1.urn.onf.core.network.module.rev160630.g_forwardingconstruct.FcPortBuilder;
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TpId;
 
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Consumer;
+
+import static org.mockito.Mockito.*;
+import static org.opendaylight.unimgr.impl.ForwardingConstructTestUtils.fcSingleNode;
+import static org.opendaylight.unimgr.impl.ForwardingConstructTestUtils.fcTwoNodes;
 
 class TestBusinessEx extends RuntimeException {
     public TestBusinessEx() {
@@ -39,20 +35,29 @@ class TestBusinessEx extends RuntimeException {
 
 /**
  * @author bartosz.michalik@amartus.com
+ * @author krzysztof.bijakowski@amartus.com [modifications]
  */
 public class FcRouteActivatorServiceTest {
 
-
     private static final TopologyId topoA = new TopologyId("a");
+
     private static final TopologyId topoZ = new TopologyId("z");
 
+    private ForwardingConstructActivationStateTracker stateTracker;
+
     public ForwardingConstructActivatorService createService(List<ActivationDriverBuilder> builders) {
         return new ForwardingConstructActivatorService(new ActivationDriverRepoServiceImpl(builders));
     }
 
+    @Before
+    public void setup() {
+        stateTracker = Mockito.mock(ForwardingConstructActivationStateTracker.class);
+        when(stateTracker.isActivatable()).thenReturn(true);
+        when(stateTracker.isDeactivatable()).thenReturn(true);
+    }
+
     @Test
     public void testActivateSingleNode() throws Exception {
-
         //having
         final ActivationDriver d1 = mock(ActivationDriver.class);
 
@@ -62,11 +67,13 @@ public class FcRouteActivatorServiceTest {
         ));
 
         //when
-        service.activate(singleNode());
+        service.activate(fcSingleNode(), stateTracker);
 
         //then
         verify(d1).activate();
         verify(d1).commit();
+        verify(stateTracker).isActivatable();
+        verify(stateTracker).activated(Mockito.any());
     }
 
     @Test
@@ -79,16 +86,17 @@ public class FcRouteActivatorServiceTest {
         ));
 
         //when
-        service.activate(twoNodes());
+        service.activate(fcTwoNodes(), stateTracker);
 
         //then
         verify(d1, times(2)).activate();
         verify(d1, times(2)).commit();
+        verify(stateTracker).isActivatable();
+        verify(stateTracker).activated(Mockito.any());
     }
 
     @Test
     public void testActivateTwoNodesMultiVendor() throws Exception {
-
         //having
         final ActivationDriver d1 = mock(ActivationDriver.class);
         final ActivationDriver d2 = mock(ActivationDriver.class);
@@ -99,18 +107,18 @@ public class FcRouteActivatorServiceTest {
         ));
 
         //when
-        service.activate(twoNodes());
-
+        service.activate(fcTwoNodes(), stateTracker);
         //then
         verify(d1).activate();
         verify(d1).commit();
         verify(d2).activate();
         verify(d2).commit();
+        verify(stateTracker).isActivatable();
+        verify(stateTracker).activated(Mockito.any());
     }
 
     @Test
     public void testActivateSingleNodeFailure() throws Exception {
-
         //having
         final ActivationDriver d1 = spy(new FailingActivationDriver(p -> { if(p.getTopology().equals(topoA)) throw new TestBusinessEx();}));
 
@@ -119,15 +127,40 @@ public class FcRouteActivatorServiceTest {
         ));
 
         //when
-        service.activate(singleNode());
+        service.activate(fcSingleNode(), stateTracker);
 
         //then
         verify(d1, times(1)).rollback();
+        verify(stateTracker).isActivatable();
+        verify(stateTracker).activationFailed(Mockito.any());
     }
 
     @Test
-    public void testActivateMultiNodeFailure() throws Exception {
+    public void testActivateFcExists() throws Exception {
+        //having
+        ForwardingConstructActivationStateTracker stateTrackerFcExists = Mockito.mock(ForwardingConstructActivationStateTracker.class);
+        when(stateTrackerFcExists.isActivatable()).thenReturn(false);
+
+        final ActivationDriver d1 = mock(ActivationDriver.class);
+
+        ForwardingConstructActivatorService service = createService(Arrays.asList(
+                ActivationDriverMocks.prepareDriver((port1, port2) -> topoA.equals(port1.getTopology()) ? d1 : null),
+                ActivationDriverMocks.prepareDriver((port1, port2) -> null)
+        ));
 
+        //when
+        service.activate(fcSingleNode(), stateTrackerFcExists);
+
+        //then
+        verify(d1, never()).activate();
+        verify(d1, never()).commit();
+        verify(stateTrackerFcExists).isActivatable();
+        verify(stateTrackerFcExists, never()).activated(Mockito.any());
+        verify(stateTrackerFcExists, never()).activationFailed(Mockito.any());
+    }
+
+    @Test
+    public void testActivateMultiNodeFailure() throws Exception {
         //having
         final ActivationDriver d1 = spy(new FailingActivationDriver(p -> { if(p.getTopology().equals(topoA)) throw new TestBusinessEx();}));
 
@@ -136,16 +169,17 @@ public class FcRouteActivatorServiceTest {
         ));
 
         //when
-        service.activate(twoNodes());
+        service.activate(fcTwoNodes(), stateTracker);
 
         //then
         verify(d1, times(1)).activate();
         verify(d1, times(2)).rollback();
+        verify(stateTracker).isActivatable();
+        verify(stateTracker).activationFailed(Mockito.any());
     }
 
     @Test
     public void testDeactivateSingleNodeFailure() throws Exception {
-
         //having
         final ActivationDriver d1 = spy(new FailingActivationDriver(p -> { if(p.getTopology().equals(topoA)) throw new TestBusinessEx();}));
 
@@ -155,11 +189,13 @@ public class FcRouteActivatorServiceTest {
         ));
 
         //when
-        service.deactivate(singleNode());
+        service.deactivate(fcSingleNode(), stateTracker);
 
         //then
         verify(d1, times(1)).deactivate();
         verify(d1, times(1)).rollback();
+        verify(stateTracker).isDeactivatable();
+        verify(stateTracker).deactivationFailed();
     }
 
     @Test
@@ -172,44 +208,42 @@ public class FcRouteActivatorServiceTest {
         ));
 
         //when
-        service.deactivate(twoNodes());
+        service.deactivate(fcTwoNodes(), stateTracker);
 
         //then
         verify(d1, times(2)).deactivate();
         verify(d1, times(2)).commit();
+        verify(stateTracker).isDeactivatable();
+        verify(stateTracker).deactivated();
     }
 
-    private ForwardingConstruct singleNode() {
-        return fc(
-                port("a", "localhost", "80"),
-                port("z", "localhost", "8080")
-        );
-    }
+    @Test
+    public void testDeactivateFcNotExists() throws Exception {
+        //having
+        ForwardingConstructActivationStateTracker stateTrackerFcNotExists = Mockito.mock(ForwardingConstructActivationStateTracker.class);
+        when(stateTrackerFcNotExists.isDeactivatable()).thenReturn(false);
 
-    private ForwardingConstruct twoNodes() {
-        return fc(
-                port("a", "192.168.1.1", "80"),
-                port("z", "192.168.1.2", "80")
-        );
-    }
+        final ActivationDriver d1 = mock(ActivationDriver.class);
 
-    private ForwardingConstruct fc(FcPort... ports) {
-        return new ForwardingConstructBuilder()
-                .setFcPort(Arrays.asList(ports))
-                .build();
-    }
+        ForwardingConstructActivatorService service = createService(Collections.singletonList(
+                ActivationDriverMocks.prepareDriver(port -> d1)
+        ));
+
+        //when
+        service.deactivate(fcTwoNodes(), stateTrackerFcNotExists);
 
-    FcPort port(String topo, String host, String port) {
-        return new FcPortBuilder()
-                .setTopology(new TopologyId(topo))
-                .setNode(new NodeId(host))
-                .setTp(new TpId(port))
-                .build();
+        //then
+        verify(d1, never()).deactivate();
+        verify(d1, never()).commit();
+        verify(stateTrackerFcNotExists).isDeactivatable();
+        verify(stateTrackerFcNotExists, never()).deactivated();
+        verify(stateTrackerFcNotExists, never()).deactivationFailed();
     }
 
     private static class FailingActivationDriver implements ActivationDriver {
 
         private final Consumer<FcPort> consumer;
+
         private FcPort from;
 
         FailingActivationDriver(Consumer<FcPort> portConsumer) {
diff --git a/impl/src/test/java/org/opendaylight/unimgr/impl/ForwardingConstructActivationStateTrackerTest.java b/impl/src/test/java/org/opendaylight/unimgr/impl/ForwardingConstructActivationStateTrackerTest.java
new file mode 100644 (file)
index 0000000..d87f7b7
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2016 Cisco Systems Inc 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.unimgr.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.binding.test.AbstractDataBrokerTest;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.yang.gen.v1.urn.mef.unimgr.ext.rev160725.ActivationStatus;
+import org.opendaylight.yang.gen.v1.urn.onf.core.network.module.rev160630.forwarding.constructs.ForwardingConstruct;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import static org.junit.Assert.*;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.opendaylight.unimgr.impl.ForwardingConstructTestUtils.fcIid;
+import static org.opendaylight.unimgr.impl.ForwardingConstructTestUtils.fcSingleNode;
+
+/**
+ * @author krzysztof.bijakowski@amartus.com
+ */
+public class ForwardingConstructActivationStateTrackerTest extends AbstractDataBrokerTest {
+
+    private InstanceIdentifier fcIid;
+
+    @Before
+    public void setUp() {
+        fcIid = fcIid();
+    }
+
+    @Test
+    public void testIsActivatablePositive() {
+        //given
+        ForwardingConstructActivationStateTracker stateTracker = createStateTracker(mockDataBroker(false));
+
+        //when
+        boolean result = stateTracker.isActivatable();
+
+        //then
+        assertTrue(result);
+    }
+
+    @Test
+    public void testIsActivatableNegative() {
+        //given
+        ForwardingConstructActivationStateTracker stateTracker = createStateTracker(mockDataBroker(true));
+
+        //when
+        boolean result = stateTracker.isActivatable();
+
+        //then
+        assertFalse(result);
+    }
+
+    @Test
+    public void testIsDeactivatablePositive() {
+        //given
+        ForwardingConstructActivationStateTracker stateTracker = createStateTracker(mockDataBroker(true));
+
+        //when
+        boolean result = stateTracker.isDeactivatable();
+
+        //then
+        assertTrue(result);
+    }
+
+    @Test
+    public void testIsDeactivatableNegative() {
+        //given
+        ForwardingConstructActivationStateTracker stateTracker = createStateTracker(mockDataBroker(false));
+
+        //when
+        boolean result = stateTracker.isDeactivatable();
+
+        //then
+        assertFalse(result);
+    }
+
+    @Test
+    public void testActivated() {
+        //given
+        DataBroker dataBroker = getDataBroker();
+        ForwardingConstructActivationStateTracker stateTracker = createStateTracker(dataBroker);
+        ForwardingConstruct exceptedFc = fcSingleNode();
+
+        //when
+        stateTracker.activated(exceptedFc);
+
+        //then
+        ReadOnlyTransaction transaction = dataBroker.newReadOnlyTransaction();
+        CheckedFuture<Optional<ForwardingConstruct>, ReadFailedException> result =
+                transaction.read(LogicalDatastoreType.OPERATIONAL, fcIid);
+        Optional<ForwardingConstruct> fcOptional = Optional.absent();
+
+        try {
+            fcOptional = result.checkedGet();
+        } catch (ReadFailedException e) {
+            fail("Error during test result verification - cannot read data : " + e.getMessage());
+        }
+
+        assertTrue(fcOptional.isPresent());
+        ForwardingConstruct actualFc = fcOptional.get();
+        ForwardingConstructTestUtils.assertEquals(exceptedFc, actualFc);
+        ForwardingConstructTestUtils.assertActivationState(actualFc, ActivationStatus.ACTIVE);
+    }
+
+    @Test
+    public void testActivationFailed() {
+        //given
+        DataBroker dataBroker = getDataBroker();
+        ForwardingConstructActivationStateTracker stateTracker = createStateTracker(dataBroker);
+        ForwardingConstruct exceptedFc = fcSingleNode();
+
+        //when
+        stateTracker.activationFailed(exceptedFc);
+
+        //then
+        ReadOnlyTransaction transaction = dataBroker.newReadOnlyTransaction();
+        CheckedFuture<Optional<ForwardingConstruct>, ReadFailedException> result =
+                transaction.read(LogicalDatastoreType.OPERATIONAL, fcIid);
+        Optional<ForwardingConstruct> fcOptional = Optional.absent();
+
+        try {
+            fcOptional = result.checkedGet();
+        } catch (ReadFailedException e) {
+            fail("Error during test result verification - cannot read data : " + e.getMessage());
+        }
+
+        assertTrue(fcOptional.isPresent());
+        ForwardingConstruct actualFc = fcOptional.get();
+        ForwardingConstructTestUtils.assertEquals(exceptedFc, actualFc);
+        ForwardingConstructTestUtils.assertActivationState(actualFc, ActivationStatus.FAILED);
+    }
+
+    @Test
+    public void testDeactivated() {
+        //given
+        DataBroker dataBroker = getDataBroker();
+        ForwardingConstructActivationStateTracker stateTracker = createStateTracker(dataBroker);
+        ForwardingConstruct fc = fcSingleNode();
+        stateTracker.activated(fc);
+
+        //when
+        stateTracker.deactivated();
+
+        //then
+        ReadOnlyTransaction transaction = dataBroker.newReadOnlyTransaction();
+        CheckedFuture<Optional<ForwardingConstruct>, ReadFailedException> result =
+                transaction.read(LogicalDatastoreType.OPERATIONAL, fcIid);
+        Optional<ForwardingConstruct> fcOptional = Optional.absent();
+
+        try {
+            fcOptional = result.checkedGet();
+        } catch (ReadFailedException e) {
+            fail("Error during test result verification - cannot read data : " + e.getMessage());
+        }
+
+        assertFalse(fcOptional.isPresent());
+    }
+
+    @Test
+    public void testDeactivationFailed() {
+        //TODO write test when implemented
+    }
+
+    private DataBroker mockDataBroker(boolean fcExists) {
+        DataBroker dataBroker = mock(DataBroker.class);
+        final ReadOnlyTransaction transaction = mock(ReadOnlyTransaction.class);
+        final CheckedFuture transactionResult = mock(CheckedFuture.class);
+        final ForwardingConstruct forwardingConstruct = Mockito.mock(ForwardingConstruct.class);
+        final Optional<ForwardingConstruct> optionalForwardingConstruct;
+
+        if(fcExists) {
+            optionalForwardingConstruct = Optional.of(forwardingConstruct);
+        } else {
+            optionalForwardingConstruct = Optional.absent();
+        }
+
+        try {
+            when(transactionResult.checkedGet()).thenReturn(optionalForwardingConstruct);
+        } catch (Exception e) {
+            fail("Cannot create mocks : " + e.getMessage());
+        }
+        when(transaction.read(Mockito.eq(LogicalDatastoreType.OPERATIONAL), any(InstanceIdentifier.class)))
+                .thenReturn(transactionResult);
+        when(dataBroker.newReadOnlyTransaction()).thenReturn(transaction);
+
+        return dataBroker;
+    }
+
+    private ForwardingConstructActivationStateTracker createStateTracker(DataBroker dataBroker) {
+        return new ForwardingConstructActivationStateTracker(dataBroker, fcIid);
+    }
+}
\ No newline at end of file
diff --git a/impl/src/test/java/org/opendaylight/unimgr/impl/ForwardingConstructTestUtils.java b/impl/src/test/java/org/opendaylight/unimgr/impl/ForwardingConstructTestUtils.java
new file mode 100644 (file)
index 0000000..aa90dae
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2016 Cisco Systems Inc 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.unimgr.impl;
+
+import org.opendaylight.yang.gen.v1.urn.mef.unimgr.ext.rev160725.ActivationStatus;
+import org.opendaylight.yang.gen.v1.urn.mef.unimgr.ext.rev160725.ForwardingConstruct1;
+import org.opendaylight.yang.gen.v1.urn.onf.core.network.module.rev160630.ForwardingConstructs;
+import org.opendaylight.yang.gen.v1.urn.onf.core.network.module.rev160630.forwarding.constructs.ForwardingConstruct;
+import org.opendaylight.yang.gen.v1.urn.onf.core.network.module.rev160630.forwarding.constructs.ForwardingConstructBuilder;
+import org.opendaylight.yang.gen.v1.urn.onf.core.network.module.rev160630.forwarding.constructs.ForwardingConstructKey;
+import org.opendaylight.yang.gen.v1.urn.onf.core.network.module.rev160630.g_forwardingconstruct.FcPort;
+import org.opendaylight.yang.gen.v1.urn.onf.core.network.module.rev160630.g_forwardingconstruct.FcPortBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TpId;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.junit.Assert;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author krzysztof.bijakowski@amartus.com
+ */
+class ForwardingConstructTestUtils {
+    private static final ForwardingConstructKey fcKey = new ForwardingConstructKey("fc");
+
+    static ForwardingConstructKey fcKey() {
+        return fcKey;
+    }
+
+    static InstanceIdentifier<ForwardingConstruct> fcIid() {
+        return InstanceIdentifier
+                .builder(ForwardingConstructs.class)
+                .child(ForwardingConstruct.class, fcKey)
+                .build();
+    }
+    static ForwardingConstruct fcSingleNode() {
+        return fc(
+                port("a", "localhost", "80"),
+                port("z", "localhost", "8080")
+        );
+    }
+
+    static ForwardingConstruct fcTwoNodes() {
+        return fc(
+                port("a", "192.168.1.1", "80"),
+                port("z", "192.168.1.2", "80")
+        );
+    }
+
+    static void assertEquals(ForwardingConstruct expectedFc, ForwardingConstruct actualFc) {
+        assertNotNull(expectedFc);
+        assertNotNull(actualFc);
+
+        assertNotNull(expectedFc.getFcPort());
+        assertNotNull(actualFc.getFcPort());
+
+        Set<FcPort> expectedFcPorts = new HashSet<>(expectedFc.getFcPort());
+        Set<FcPort> actualFcPorts = new HashSet<>(actualFc.getFcPort());
+
+        assertTrue(expectedFcPorts.size() == actualFcPorts.size());
+
+        for (FcPort expectedFcPort : expectedFcPorts) {
+            boolean equal = false;
+
+            for (FcPort actualFcPort : actualFcPorts) {
+                equal = compareFcPort(expectedFcPort, actualFcPort);
+
+                if(equal) {
+                    break;
+                }
+            }
+
+            assertTrue(equal);
+        }
+        //TODO assertions for other parameters
+    }
+
+    static void assertActivationState(ForwardingConstruct fc, ActivationStatus expectedActivationStatus) {
+        assertNotNull(fc.getAugmentation(ForwardingConstruct1.class));
+        assertNotNull((fc.getAugmentation(ForwardingConstruct1.class).getUnimgrAttrs()));
+
+        ActivationStatus actualActivationStatus = fc.getAugmentation(ForwardingConstruct1.class).getUnimgrAttrs().getStatus();
+        assertNotNull(actualActivationStatus);
+
+        Assert.assertEquals(expectedActivationStatus, actualActivationStatus);
+    }
+
+    private static boolean compareFcPort(FcPort expectedFcPort, FcPort actualFcPort) {
+        assertNotNull(expectedFcPort);
+        assertNotNull(actualFcPort);
+
+        assertNotNull(expectedFcPort.getTopology());
+        assertNotNull(expectedFcPort.getTopology().getValue());
+        assertNotNull(actualFcPort.getTopology());
+        assertNotNull(actualFcPort.getTopology().getValue());
+
+        assertNotNull(expectedFcPort.getNode());
+        assertNotNull(expectedFcPort.getNode().getValue());
+        assertNotNull(actualFcPort.getNode());
+        assertNotNull(actualFcPort.getNode().getValue());
+
+        assertNotNull(expectedFcPort.getTp());
+        assertNotNull(expectedFcPort.getTp().getValue());
+        assertNotNull(actualFcPort.getTp());
+        assertNotNull(actualFcPort.getTp().getValue());
+
+        //TODO assertions for other parameters
+        //TODO add possibility of null paramaters
+
+        boolean result =
+            expectedFcPort.getTopology().getValue().equals(actualFcPort.getTopology().getValue()) &&
+            expectedFcPort.getNode().getValue().equals(actualFcPort.getNode().getValue()) &&
+            expectedFcPort.getTp().getValue().equals(actualFcPort.getTp().getValue());
+
+        return result;
+    }
+
+    private static ForwardingConstruct fc(FcPort... ports) {
+        return new ForwardingConstructBuilder()
+                .setFcPort(Arrays.asList(ports))
+                .setKey(fcKey)
+                .build();
+    }
+
+    private static FcPort port(String topo, String host, String port) {
+        return new FcPortBuilder()
+                .setTopology(new TopologyId(topo))
+                .setNode(new NodeId(host))
+                .setTp(new TpId(port))
+                .build();
+    }
+}
diff --git a/presto-api/src/main/yang/mef-unimgr-ext.yang b/presto-api/src/main/yang/mef-unimgr-ext.yang
new file mode 100644 (file)
index 0000000..a2a9827
--- /dev/null
@@ -0,0 +1,29 @@
+module mef-unimgr-ext {
+  namespace "urn:mef:unimgr-ext";
+  prefix mef-unimgr-ext;
+
+  import onf-core-network-module {
+    prefix onf-cn;
+  }
+
+  revision 2016-07-25 {
+  }
+
+  typedef ActivationStatus {
+    type enumeration {
+      enum INACTIVE;
+      enum ACTIVE;
+      enum FAILED;
+    }
+  }
+
+  augment "/onf-cn:forwarding-constructs/onf-cn:forwarding-construct" {
+    container unimgr-attrs {
+      leaf status {
+        type ActivationStatus;
+        config false;
+        default INACTIVE;
+      }
+    }
+  }
+}