From 38edbd609486f29e3d0b0998f45f48ae079eb729 Mon Sep 17 00:00:00 2001 From: Krzysztof Bijakowski Date: Thu, 28 Jul 2016 12:18:43 +0200 Subject: [PATCH] Activation status handling mechanism for ForwardingConstruct provisioning Change-Id: Ibbc85900c6205e2f09a8a2608afe7abb2b322bf4 Signed-off-by: Krzysztof Bijakowski --- .../activator/L2vpnBridgeActivatorTest.java | 16 +- impl/pom.xml | 13 ++ ...ardingConstructActivationStateTracker.java | 113 ++++++++++ .../ForwardingConstructActivatorService.java | 47 +++- .../ForwardingConstructChangeListener.java | 51 +++-- .../mef/nrp/impl/ActivationTransaction.java | 50 ++++- .../impl/FcRouteActivatorServiceTest.java | 136 +++++++----- ...ngConstructActivationStateTrackerTest.java | 206 ++++++++++++++++++ .../impl/ForwardingConstructTestUtils.java | 143 ++++++++++++ presto-api/src/main/yang/mef-unimgr-ext.yang | 29 +++ 10 files changed, 721 insertions(+), 83 deletions(-) create mode 100644 impl/src/main/java/org/opendaylight/unimgr/impl/ForwardingConstructActivationStateTracker.java create mode 100644 impl/src/test/java/org/opendaylight/unimgr/impl/ForwardingConstructActivationStateTrackerTest.java create mode 100644 impl/src/test/java/org/opendaylight/unimgr/impl/ForwardingConstructTestUtils.java create mode 100644 presto-api/src/main/yang/mef-unimgr-ext.yang diff --git a/cisco-xr-driver/src/test/java/org/opendaylight/unimgr/mef/nrp/cisco/xr/l2vpn/activator/L2vpnBridgeActivatorTest.java b/cisco-xr-driver/src/test/java/org/opendaylight/unimgr/mef/nrp/cisco/xr/l2vpn/activator/L2vpnBridgeActivatorTest.java index dfb556d7..5a5e01e5 100644 --- a/cisco-xr-driver/src/test/java/org/opendaylight/unimgr/mef/nrp/cisco/xr/l2vpn/activator/L2vpnBridgeActivatorTest.java +++ b/cisco-xr-driver/src/test/java/org/opendaylight/unimgr/mef/nrp/cisco/xr/l2vpn/activator/L2vpnBridgeActivatorTest.java @@ -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 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."); } diff --git a/impl/pom.xml b/impl/pom.xml index 4998f091..9c3eee09 100644 --- a/impl/pom.xml +++ b/impl/pom.xml @@ -76,6 +76,19 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL 1.1.0-SNAPSHOT + + + org.opendaylight.controller + sal-binding-broker-impl + test + + + org.opendaylight.controller + sal-binding-broker-impl + test-jar + test + + junit 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 index 00000000..aba25664 --- /dev/null +++ b/impl/src/main/java/org/opendaylight/unimgr/impl/ForwardingConstructActivationStateTracker.java @@ -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 fcIid; + + public ForwardingConstructActivationStateTracker(DataBroker dataBroker, InstanceIdentifier fcIid) { + this.dataBroker = dataBroker; + this.fcIid = fcIid; + } + + public boolean isActivatable() { + ReadOnlyTransaction tx = dataBroker.newReadOnlyTransaction(); + + try { + CheckedFuture, ReadFailedException> result = tx.read(LogicalDatastoreType.OPERATIONAL, fcIid); + Optional 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); + } + } +} diff --git a/impl/src/main/java/org/opendaylight/unimgr/impl/ForwardingConstructActivatorService.java b/impl/src/main/java/org/opendaylight/unimgr/impl/ForwardingConstructActivatorService.java index 43f853a3..274a8661 100644 --- a/impl/src/main/java/org/opendaylight/unimgr/impl/ForwardingConstructActivatorService.java +++ b/impl/src/main/java/org/opendaylight/unimgr/impl/ForwardingConstructActivatorService.java @@ -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 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 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 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 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); + } } } diff --git a/impl/src/main/java/org/opendaylight/unimgr/impl/ForwardingConstructChangeListener.java b/impl/src/main/java/org/opendaylight/unimgr/impl/ForwardingConstructChangeListener.java index 729d847f..44af5d31 100644 --- a/impl/src/main/java/org/opendaylight/unimgr/impl/ForwardingConstructChangeListener.java +++ b/impl/src/main/java/org/opendaylight/unimgr/impl/ForwardingConstructChangeListener.java @@ -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, AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(ForwardingConstructChangeListener.class); + private final ListenerRegistration 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 fwPath = getFwConstructsPath(); + final InstanceIdentifier fwPath = getFcPath(); final DataTreeIdentifier dataTreeIid = new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, fwPath); listener = dataBroker.registerDataTreeChangeListener(dataTreeIid, this); @@ -59,6 +60,7 @@ public class ForwardingConstructChangeListener implements DataTreeChangeListener } for (final DataTreeModification change : collection) { final DataObjectModification root = change.getRootNode(); + switch (root.getModificationType()) { case SUBTREE_MODIFIED: update(change); @@ -86,14 +88,19 @@ public class ForwardingConstructChangeListener implements DataTreeChangeListener protected void add(DataTreeModification 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 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 getFwConstructsPath() { + private InstanceIdentifier getFcPath() { return InstanceIdentifier .builder(ForwardingConstructs.class).child(ForwardingConstruct.class).build(); } + + private InstanceIdentifier getFcIid(DataTreeModification fcModification) { + return fcModification.getRootPath().getRootIdentifier(); + } + + private ForwardingConstruct getFcForActivation(DataTreeModification fcModification) { + return fcModification.getRootNode().getDataAfter(); + } + + private ForwardingConstruct getFcForDeactivation(DataTreeModification fcModification) { + return fcModification.getRootNode().getDataBefore(); + } + + + private ForwardingConstructActivationStateTracker getFcActivationStateTracker( + DataTreeModification fcModification) { + return new ForwardingConstructActivationStateTracker(dataBroker, getFcIid(fcModification)); + } } diff --git a/impl/src/main/java/org/opendaylight/unimgr/mef/nrp/impl/ActivationTransaction.java b/impl/src/main/java/org/opendaylight/unimgr/mef/nrp/impl/ActivationTransaction.java index 1d0c6af4..9cef7c02 100644 --- a/impl/src/main/java/org/opendaylight/unimgr/mef/nrp/impl/ActivationTransaction.java +++ b/impl/src/main/java/org/opendaylight/unimgr/mef/nrp/impl/ActivationTransaction.java @@ -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 drivers = new ArrayList<>(); + private List 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 message; + + private Optional cause; + + private Result(boolean successful, Optional message, Optional cause) { + this.successful = successful; + this.message = message; + this.cause = cause; + } + + public Optional getCause() { + return cause; + } + + public boolean isSuccessful() { + return successful; + } + + public Optional 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)); + } + } + } diff --git a/impl/src/test/java/org/opendaylight/unimgr/impl/FcRouteActivatorServiceTest.java b/impl/src/test/java/org/opendaylight/unimgr/impl/FcRouteActivatorServiceTest.java index 360ace17..4df47c33 100644 --- a/impl/src/test/java/org/opendaylight/unimgr/impl/FcRouteActivatorServiceTest.java +++ b/impl/src/test/java/org/opendaylight/unimgr/impl/FcRouteActivatorServiceTest.java @@ -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 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 consumer; + private FcPort from; FailingActivationDriver(Consumer 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 index 00000000..d87f7b78 --- /dev/null +++ b/impl/src/test/java/org/opendaylight/unimgr/impl/ForwardingConstructActivationStateTrackerTest.java @@ -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, ReadFailedException> result = + transaction.read(LogicalDatastoreType.OPERATIONAL, fcIid); + Optional 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, ReadFailedException> result = + transaction.read(LogicalDatastoreType.OPERATIONAL, fcIid); + Optional 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, ReadFailedException> result = + transaction.read(LogicalDatastoreType.OPERATIONAL, fcIid); + Optional 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 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 index 00000000..aa90dae2 --- /dev/null +++ b/impl/src/test/java/org/opendaylight/unimgr/impl/ForwardingConstructTestUtils.java @@ -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 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 expectedFcPorts = new HashSet<>(expectedFc.getFcPort()); + Set 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 index 00000000..a2a98277 --- /dev/null +++ b/presto-api/src/main/yang/mef-unimgr-ext.yang @@ -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; + } + } + } +} -- 2.36.6