From 8ff15d56c9b671371c954d2e0110917ae1dc020e Mon Sep 17 00:00:00 2001 From: Jozef Gloncak Date: Wed, 19 Aug 2015 12:13:17 +0200 Subject: [PATCH] BUG-4084: Li:Save groups in operational DS based on barrier success ItemLifeCycleListener also added to SalGroupServiceImpl class to be able persist information about add/delete/update of grouping to operational DS. Change-Id: Ia2347af3133d7c68f54f206683d5dc981e81f9dd Signed-off-by: Jozef Gloncak Signed-off-by: Michal Rehak --- .../impl/services/SalGroupServiceImpl.java | 111 +++++++++++++++++- .../services/SalGroupServiceImplTest.java | 75 +++++++++++- .../services/SalMeterServiceImplTest.java | 30 +++-- .../impl/services/ServiceMocking.java | 39 +++++- 4 files changed, 232 insertions(+), 23 deletions(-) diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/services/SalGroupServiceImpl.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/services/SalGroupServiceImpl.java index 2a6b134b70..1e0dcd7e52 100644 --- a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/services/SalGroupServiceImpl.java +++ b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/services/SalGroupServiceImpl.java @@ -8,8 +8,22 @@ package org.opendaylight.openflowplugin.impl.services; import java.util.concurrent.Future; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext; import org.opendaylight.openflowplugin.api.openflow.device.RequestContextStack; +import org.opendaylight.openflowplugin.api.openflow.registry.flow.FlowDescriptor; +import org.opendaylight.openflowplugin.api.openflow.rpc.ItemLifeCycleSource; +import org.opendaylight.openflowplugin.api.openflow.rpc.listener.ItemLifecycleListener; +import org.opendaylight.openflowplugin.impl.registry.flow.FlowDescriptorFactory; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupOutput; import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.RemoveGroupInput; @@ -18,33 +32,122 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.Sal import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.UpdateGroupInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.UpdateGroupOutput; import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.Group; +import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.GroupBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.GroupKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey; +import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier; import org.opendaylight.yangtools.yang.common.RpcResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nullable; -public class SalGroupServiceImpl implements SalGroupService { +public class SalGroupServiceImpl implements SalGroupService, ItemLifeCycleSource { + private static final Logger LOG = LoggerFactory.getLogger(SalGroupServiceImpl.class); private final GroupService addGroup; private final GroupService updateGroup; private final GroupService removeGroup; + private final DeviceContext deviceContext; + private ItemLifecycleListener itemLifecycleListener; public SalGroupServiceImpl(final RequestContextStack requestContextStack, final DeviceContext deviceContext) { + this.deviceContext = deviceContext; addGroup = new GroupService<>(requestContextStack, deviceContext, AddGroupOutput.class); updateGroup = new GroupService<>(requestContextStack, deviceContext, UpdateGroupOutput.class); removeGroup = new GroupService<>(requestContextStack, deviceContext, RemoveGroupOutput.class); } + @Override + public void setItemLifecycleListener(@Nullable ItemLifecycleListener itemLifecycleListener) { + this.itemLifecycleListener = itemLifecycleListener; + } + @Override public Future> addGroup(final AddGroupInput input) { addGroup.getDeviceContext().getDeviceGroupRegistry().store(input.getGroupId()); - return addGroup.handleServiceCall(input); + final ListenableFuture> resultFuture = addGroup.handleServiceCall(input); + Futures.addCallback(resultFuture, new FutureCallback>() { + @Override + public void onSuccess(RpcResult result) { + if (result.isSuccessful()) { + LOG.debug("group add finished without error, id={}", input.getGroupId().getValue()); + addIfNecessaryToDS(input.getGroupId(), input); + } + } + + @Override + public void onFailure(Throwable t) { + LOG.error("group add failed for id={}. Exception: {}", input.getGroupId().getValue(), t); + } + }); + + return resultFuture; } + @Override public Future> updateGroup(final UpdateGroupInput input) { - return updateGroup.handleServiceCall(input.getUpdatedGroup()); + final ListenableFuture> resultFuture = updateGroup.handleServiceCall(input.getUpdatedGroup()); + Futures.addCallback(resultFuture, new FutureCallback>() { + @Override + public void onSuccess(@Nullable RpcResult result) { + if (result.isSuccessful()) { + LOG.debug("Group update succeded"); + removeIfNecessaryFromDS(input.getOriginalGroup().getGroupId()); + addIfNecessaryToDS(input.getUpdatedGroup().getGroupId(), input.getUpdatedGroup()); + } + } + + @Override + public void onFailure(Throwable t) { + LOG.debug("Group update failed for id={}. Exception: {}", input.getOriginalGroup().getGroupId(), t); + } + }); + return resultFuture; } @Override public Future> removeGroup(final RemoveGroupInput input) { removeGroup.getDeviceContext().getDeviceGroupRegistry().markToBeremoved(input.getGroupId()); - return removeGroup.handleServiceCall(input); + final ListenableFuture> resultFuture = removeGroup.handleServiceCall(input); + Futures.addCallback(resultFuture, new FutureCallback>() { + @Override + public void onSuccess(@Nullable RpcResult result) { + if (result.isSuccessful()) { + LOG.debug("Group remove succeded"); + removeIfNecessaryFromDS(input.getGroupId()); + } + } + + @Override + public void onFailure(Throwable t) { + LOG.error("Group remove failed for id={}. Exception: {}", input.getGroupId(), t); + } + }); + return resultFuture; + } + + private void removeIfNecessaryFromDS(final GroupId groupId) { + if (itemLifecycleListener != null) { + KeyedInstanceIdentifier groupPath + = createGroupPath(groupId, + deviceContext.getDeviceState().getNodeInstanceIdentifier()); + itemLifecycleListener.onRemoved(groupPath); + } + } + + private void addIfNecessaryToDS(final GroupId groupId, final Group data) { + if (itemLifecycleListener != null) { + KeyedInstanceIdentifier groupPath + = createGroupPath(groupId, + deviceContext.getDeviceState().getNodeInstanceIdentifier()); + itemLifecycleListener.onAdded(groupPath, new GroupBuilder(data).build()); + } + } + + static KeyedInstanceIdentifier createGroupPath(final GroupId groupId, final KeyedInstanceIdentifier nodePath) { + return nodePath.augmentation(FlowCapableNode.class).child(org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group.class, new GroupKey(groupId)); } } diff --git a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/services/SalGroupServiceImplTest.java b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/services/SalGroupServiceImplTest.java index 379754612f..cdafaca8fb 100644 --- a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/services/SalGroupServiceImplTest.java +++ b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/services/SalGroupServiceImplTest.java @@ -1,11 +1,23 @@ package org.opendaylight.openflowplugin.impl.services; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; import org.junit.Test; +import org.mockito.Matchers; import org.mockito.Mock; import org.opendaylight.openflowplugin.api.openflow.registry.group.DeviceGroupRegistry; +import org.opendaylight.openflowplugin.api.openflow.rpc.listener.ItemLifecycleListener; import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.*; +import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.group.update.OriginalGroup; +import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.group.update.OriginalGroupBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.group.update.UpdatedGroup; +import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.group.update.UpdatedGroupBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group; +import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.GroupKey; +import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; @@ -15,43 +27,98 @@ import static org.mockito.Mockito.when; public class SalGroupServiceImplTest extends ServiceMocking { private static final Long DUMMY_GROUP_ID = 15L; - public SalGroupService salGroupService; @Mock DeviceGroupRegistry mockedDeviceGroupRegistry; + SalGroupServiceImpl salGroupService; + @Override public void initialization() { super.initialization(); - salGroupService = new SalGroupServiceImpl(mockedRequestContextStack, mockedDeviceContext); when(mockedDeviceContext.getDeviceGroupRegistry()).thenReturn(mockedDeviceGroupRegistry); + salGroupService = new SalGroupServiceImpl(mockedRequestContextStack, mockedDeviceContext); } @Test public void testAddGroup() throws Exception { + addGroup(null); + } + + @Test + public void testAddGroupWithItemLifecycle() throws Exception { + addGroup(mock(ItemLifecycleListener.class)); + } + + private void addGroup(final ItemLifecycleListener itemLifecycleListener) { final GroupId dummyGroupId = new GroupId(DUMMY_GROUP_ID); AddGroupInput addGroupInput = new AddGroupInputBuilder().setGroupId(dummyGroupId).build(); + this.mockSuccessfulFuture(); + + salGroupService.setItemLifecycleListener(itemLifecycleListener); + salGroupService.addGroup(addGroupInput); verify(mockedRequestContextStack).createRequestContext(); verify(mockedDeviceGroupRegistry).store(eq(dummyGroupId)); + + if (itemLifecycleListener != null) { + verify(itemLifecycleListener).onAdded(Matchers.>any(),Matchers.any()); + } } @Test public void testUpdateGroup() throws Exception { - UpdatedGroup mockedUptatedGroup = mock(UpdatedGroup.class); - final UpdateGroupInput updateGroupInput = new UpdateGroupInputBuilder().setUpdatedGroup(mockedUptatedGroup).build(); + updateGroup(null); + } + + @Test + public void testUpdateGroupWithItemLifecycle() throws Exception { + updateGroup(mock(ItemLifecycleListener.class)); + } + + private void updateGroup(final ItemLifecycleListener itemLifecycleListener) { + final UpdatedGroup updatedGroup = new UpdatedGroupBuilder().setGroupId(new GroupId(DUMMY_GROUP_ID)).build(); + final OriginalGroup originalGroup = new OriginalGroupBuilder().setGroupId(new GroupId(DUMMY_GROUP_ID)).build(); + final UpdateGroupInput updateGroupInput = new UpdateGroupInputBuilder().setUpdatedGroup(updatedGroup).setOriginalGroup(originalGroup).build(); + + this.mockSuccessfulFuture(); + + salGroupService.setItemLifecycleListener(itemLifecycleListener); + salGroupService.updateGroup(updateGroupInput); verify(mockedRequestContextStack).createRequestContext(); + + if (itemLifecycleListener != null) { + verify(itemLifecycleListener).onAdded(Matchers.>any(),Matchers.any()); + verify(itemLifecycleListener).onRemoved(Matchers.>any()); + } } @Test public void testRemoveGroup() throws Exception { + removeGroup(null); + } + + @Test + public void testRemoveGroupWithItemLifecycle() throws Exception { + removeGroup(mock(ItemLifecycleListener.class)); + } + + private void removeGroup(final ItemLifecycleListener itemLifecycleListener) throws Exception { final GroupId dummyGroupId = new GroupId(DUMMY_GROUP_ID); RemoveGroupInput removeGroupInput = new RemoveGroupInputBuilder().setGroupId(dummyGroupId).build(); + this.mockSuccessfulFuture(); + + salGroupService.setItemLifecycleListener(itemLifecycleListener); + salGroupService.removeGroup(removeGroupInput); verify(mockedRequestContextStack).createRequestContext(); verify(mockedDeviceGroupRegistry).markToBeremoved(eq(dummyGroupId)); + + if (itemLifecycleListener != null) { + verify(itemLifecycleListener).onRemoved(Matchers.>any()); + } } } \ No newline at end of file diff --git a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/services/SalMeterServiceImplTest.java b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/services/SalMeterServiceImplTest.java index a0b0299e61..f0406f3472 100644 --- a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/services/SalMeterServiceImplTest.java +++ b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/services/SalMeterServiceImplTest.java @@ -1,21 +1,24 @@ package org.opendaylight.openflowplugin.impl.services; -import junit.framework.TestCase; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + import org.junit.Test; import org.mockito.Mock; -import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext; -import org.opendaylight.openflowplugin.api.openflow.device.RequestContextStack; +import org.mockito.Mockito; import org.opendaylight.openflowplugin.api.openflow.registry.meter.DeviceMeterRegistry; -import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.*; +import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.AddMeterInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.AddMeterInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.RemoveMeterInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.RemoveMeterInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.SalMeterService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.UpdateMeterInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.UpdateMeterInputBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.meter.update.UpdatedMeter; import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterId; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - public class SalMeterServiceImplTest extends ServiceMocking { private static final Long DUMMY_METER_ID = 15L; @@ -43,8 +46,11 @@ public class SalMeterServiceImplTest extends ServiceMocking { @Test public void testUpdateMeter() throws Exception { - UpdatedMeter mockedUptatedMeter = mock(UpdatedMeter.class); - final UpdateMeterInput updateMeterInput = new UpdateMeterInputBuilder().setUpdatedMeter(mockedUptatedMeter).build(); + final UpdatedMeter mockedUpdatedMeter = mock(UpdatedMeter.class); + Mockito.when(mockedUpdatedMeter.getMeterId()).thenReturn(new MeterId(43L)); + Mockito.doReturn(UpdatedMeter.class).when(mockedUpdatedMeter).getImplementedInterface(); + + final UpdateMeterInput updateMeterInput = new UpdateMeterInputBuilder().setUpdatedMeter(mockedUpdatedMeter).build(); salMeterService.updateMeter(updateMeterInput); verify(mockedRequestContextStack).createRequestContext(); } diff --git a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/services/ServiceMocking.java b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/services/ServiceMocking.java index 71d17ccf54..d87792b578 100644 --- a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/services/ServiceMocking.java +++ b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/services/ServiceMocking.java @@ -1,20 +1,30 @@ package org.opendaylight.openflowplugin.impl.services; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; import io.netty.util.HashedWheelTimer; import org.junit.Before; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter; +import org.opendaylight.openflowjava.protocol.api.connection.OutboundQueue; import org.opendaylight.openflowplugin.api.OFConstants; import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext; -import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext; -import org.opendaylight.openflowplugin.api.openflow.device.DeviceState; -import org.opendaylight.openflowplugin.api.openflow.device.RequestContextStack; +import org.opendaylight.openflowplugin.api.openflow.device.*; import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceInitializationPhaseHandler; import org.opendaylight.openflowplugin.api.openflow.statistics.ofpspecific.MessageSpy; import org.opendaylight.openflowplugin.impl.registry.flow.DeviceFlowRegistryImpl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.AddMeterOutput; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FeaturesReply; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; import java.math.BigInteger; @@ -25,6 +35,13 @@ import static org.mockito.Mockito.when; public abstract class ServiceMocking { private static final BigInteger DUMMY_DATAPATH_ID = new BigInteger("444"); private static final Short DUMMY_VERSION = OFConstants.OFP_VERSION_1_3; + private static final Long DUMMY_XID_VALUE = 2121L; + private static final Xid DUMMY_XID = new Xid(DUMMY_XID_VALUE); + + private static final String DUMMY_NODE_ID = "dummyNodeID"; + private static final KeyedInstanceIdentifier NODE_II + = InstanceIdentifier.create(Nodes.class).child(Node.class, new NodeKey(new NodeId(DUMMY_NODE_ID))); + @Mock RequestContextStack mockedRequestContextStack; @@ -42,15 +59,25 @@ public abstract class ServiceMocking { DeviceState mockedDeviceState; @Mock DeviceInitializationPhaseHandler mockedDevicePhaseHandler; + @Mock + RequestContext mockedRequestContext; + @Mock + OutboundQueue mockedOutboundQueue; @Before public void initialization() { + when(mockedRequestContextStack.createRequestContext()).thenReturn(mockedRequestContext); + when(mockedRequestContext.getXid()).thenReturn(DUMMY_XID); + when(mockedFeatures.getDatapathId()).thenReturn(DUMMY_DATAPATH_ID); when(mockedFeatures.getVersion()).thenReturn(DUMMY_VERSION); when(mockedPrimConnectionContext.getFeatures()).thenReturn(mockedFeatures); when(mockedPrimConnectionContext.getConnectionAdapter()).thenReturn(mockedConnectionAdapter); when(mockedPrimConnectionContext.getConnectionState()).thenReturn(ConnectionContext.CONNECTION_STATE.WORKING); + when(mockedPrimConnectionContext.getOutboundQueueProvider()).thenReturn(mockedOutboundQueue); + + when(mockedDeviceState.getNodeInstanceIdentifier()).thenReturn(NODE_II); when(mockedDeviceContext.getPrimaryConnectionContext()).thenReturn(mockedPrimConnectionContext); when(mockedDeviceContext.getMessageSpy()).thenReturn(mockedMessagSpy); @@ -59,4 +86,10 @@ public abstract class ServiceMocking { when(mockedDeviceContext.getTimer()).thenReturn(mock(HashedWheelTimer.class)); } + + protected void mockSuccessfulFuture() { + ListenableFuture> dummySuccessfulFuture = Futures.immediateFuture(RpcResultBuilder.success((T) null).build()); + when(mockedRequestContext.getFuture()).thenReturn(dummySuccessfulFuture); + } + } -- 2.36.6