Remove blueprint from bgp-openconfig-state 64/96764/7
authorRobert Varga <robert.varga@pantheon.tech>
Thu, 1 Jul 2021 12:03:05 +0000 (14:03 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Fri, 2 Jul 2021 09:23:02 +0000 (11:23 +0200)
We have some amount of configuration here, but that is rectified really
easily. Move the configuration for Config Admin and use OSGi DS instead
of blueprint.

The test suite is updated with a few FIXMEs around predictable
execution.

JIRA: BGPCEP-956
Change-Id: I3b0fe17a3dc1b685698730f897ae77c470a87ac8
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
bgp/openconfig-state/pom.xml
bgp/openconfig-state/src/main/java/org/opendaylight/protocol/bgp/state/StateProviderImpl.java
bgp/openconfig-state/src/main/resources/OSGI-INF/blueprint/bgp-openconfig-state.xml [deleted file]
bgp/openconfig-state/src/main/yang/bgp-state-config.yang [deleted file]
bgp/openconfig-state/src/test/java/org/opendaylight/protocol/bgp/state/PeerGroupUtilTest.java
bgp/openconfig-state/src/test/java/org/opendaylight/protocol/bgp/state/StateProviderImplTest.java

index cbc3699466ee16545c24aabf166bb7a23c1b848d..92112c4b70ba9ed36eaf1a0be5fbaf48a1aafdbf 100644 (file)
@@ -48,8 +48,8 @@
             <artifactId>yang-common</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.opendaylight.yangtools</groupId>
-            <artifactId>concepts</artifactId>
+            <groupId>org.opendaylight.mdsal</groupId>
+            <artifactId>yang-binding</artifactId>
         </dependency>
         <dependency>
             <groupId>org.opendaylight.mdsal</groupId>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.guicedee.services</groupId>
+            <artifactId>javax.inject</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <scope>provided</scope>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>osgi.cmpn</artifactId>
+        </dependency>
+
         <!-- test scope dependencies -->
         <dependency>
             <groupId>org.opendaylight.mdsal</groupId>
index 30eebed46b6f4987f1e7d5ad31200b3d03a7a4bd..44e377e95fd52b0f13105c8b04840ebceafab59e 100644 (file)
@@ -8,8 +8,8 @@
 package org.opendaylight.protocol.bgp.state;
 
 import static java.util.Objects.requireNonNull;
-import static java.util.concurrent.TimeUnit.SECONDS;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.MoreExecutors;
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
@@ -22,14 +22,19 @@ import java.util.TimerTask;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.stream.Collectors;
+import javax.annotation.PreDestroy;
+import javax.inject.Inject;
+import javax.inject.Singleton;
 import org.checkerframework.checker.lock.qual.GuardedBy;
 import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.binding.api.DataBroker;
 import org.opendaylight.mdsal.binding.api.Transaction;
 import org.opendaylight.mdsal.binding.api.TransactionChain;
 import org.opendaylight.mdsal.binding.api.TransactionChainListener;
+import org.opendaylight.mdsal.binding.api.WriteOperations;
 import org.opendaylight.mdsal.binding.api.WriteTransaction;
 import org.opendaylight.mdsal.common.api.CommitInfo;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
@@ -54,34 +59,65 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.RibKey;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.RequireServiceComponentRuntime;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.Designate;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 // This class is thread-safe
+@Singleton
+@Component(service = {})
+@Designate(ocd = StateProviderImpl.Configuration.class)
+@RequireServiceComponentRuntime
 public final class StateProviderImpl implements TransactionChainListener, AutoCloseable {
+    @ObjectClassDefinition
+    public static @interface Configuration {
+        @AttributeDefinition(description = "Name of the OpenConfig network instance to which to bind")
+        String networkInstanceName() default "global-bgp";
+
+        @AttributeDefinition(description = "Statistics update interval, in seconds", min = "1")
+        int updateIntervalSeconds() default 5;
+    }
+
     private static final Logger LOG = LoggerFactory.getLogger(StateProviderImpl.class);
+
     private final BGPStateConsumer stateCollector;
     private final BGPTableTypeRegistryConsumer bgpTableTypeRegistry;
     private final KeyedInstanceIdentifier<NetworkInstance, NetworkInstanceKey> networkInstanceIId;
-    private final int timeout;
     private final DataBroker dataBroker;
     @GuardedBy("this")
     private final Map<String, InstanceIdentifier<Bgp>> instanceIdentifiersCache = new HashMap<>();
     @GuardedBy("this")
     private TransactionChain transactionChain;
     @GuardedBy("this")
-    private ScheduledFuture<?> scheduleTask;
+    private final ScheduledFuture<?> scheduleTask;
     private final ScheduledExecutorService scheduler;
     private final AtomicBoolean closed = new AtomicBoolean(false);
 
+    @Activate
+    public StateProviderImpl(@Reference final @NonNull DataBroker dataBroker,
+            @Reference final @NonNull BGPTableTypeRegistryConsumer bgpTableTypeRegistry,
+            @Reference final @NonNull BGPStateConsumer stateCollector, final @NonNull Configuration configuration) {
+        this(dataBroker, configuration.updateIntervalSeconds(), bgpTableTypeRegistry, stateCollector,
+            configuration.networkInstanceName());
+    }
+
+    @Inject
     public StateProviderImpl(final @NonNull DataBroker dataBroker, final int timeout,
             final @NonNull BGPTableTypeRegistryConsumer bgpTableTypeRegistry,
             final @NonNull BGPStateConsumer stateCollector, final @NonNull String networkInstanceName) {
-        this(dataBroker, timeout, bgpTableTypeRegistry, stateCollector, networkInstanceName,
+        this(dataBroker, timeout, TimeUnit.SECONDS, bgpTableTypeRegistry, stateCollector, networkInstanceName,
                 Executors.newScheduledThreadPool(1));
     }
 
-    public StateProviderImpl(final @NonNull DataBroker dataBroker, final int timeout,
+    @VisibleForTesting
+    StateProviderImpl(final @NonNull DataBroker dataBroker, final long period, final TimeUnit timeUnit,
             final @NonNull BGPTableTypeRegistryConsumer bgpTableTypeRegistry,
             final @NonNull BGPStateConsumer stateCollector,
             final @NonNull String networkInstanceName, final @NonNull ScheduledExecutorService scheduler) {
@@ -90,46 +126,44 @@ public final class StateProviderImpl implements TransactionChainListener, AutoCl
         this.stateCollector = requireNonNull(stateCollector);
         this.networkInstanceIId = InstanceIdentifier.create(NetworkInstances.class)
                 .child(NetworkInstance.class, new NetworkInstanceKey(networkInstanceName));
-        this.timeout = timeout;
         this.scheduler = scheduler;
-    }
 
-    public synchronized void init() {
         this.transactionChain = this.dataBroker.createMergingTransactionChain(this);
         final TimerTask task = new TimerTask() {
             @Override
             @SuppressWarnings("checkstyle:IllegalCatch")
             public void run() {
                 synchronized (StateProviderImpl.this) {
-                    final WriteTransaction wTx = StateProviderImpl.this.transactionChain.newWriteOnlyTransaction();
+                    final WriteTransaction wTx = transactionChain.newWriteOnlyTransaction();
                     try {
                         updateBGPStats(wTx);
-
-                        wTx.commit().addCallback(new FutureCallback<CommitInfo>() {
-                            @Override
-                            public void onSuccess(final CommitInfo result) {
-                                LOG.debug("Successfully committed BGP stats update");
-                            }
-
-                            @Override
-                            public void onFailure(final Throwable ex) {
-                                LOG.error("Failed to commit BGP stats update", ex);
-                            }
-                        }, MoreExecutors.directExecutor());
                     } catch (final Exception e) {
                         LOG.warn("Failed to prepare Tx for BGP stats update", e);
                         wTx.cancel();
+                        return;
                     }
+
+                    wTx.commit().addCallback(new FutureCallback<CommitInfo>() {
+                        @Override
+                        public void onSuccess(final CommitInfo result) {
+                            LOG.debug("Successfully committed BGP stats update");
+                        }
+
+                        @Override
+                        public void onFailure(final Throwable ex) {
+                            LOG.error("Failed to commit BGP stats update", ex);
+                        }
+                    }, MoreExecutors.directExecutor());
                 }
             }
         };
 
-        this.scheduleTask = this.scheduler.scheduleAtFixedRate(task, 0, this.timeout, SECONDS);
+        this.scheduleTask = this.scheduler.scheduleAtFixedRate(task, 0, period, timeUnit);
     }
 
     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
             justification = "https://github.com/spotbugs/spotbugs/issues/811")
-    private synchronized void updateBGPStats(final WriteTransaction wtx) {
+    private synchronized void updateBGPStats(final WriteOperations wtx) {
         final Set<String> oldStats = new HashSet<>(this.instanceIdentifiersCache.keySet());
         this.stateCollector.getRibStats().stream().filter(BGPRibState::isActive).forEach(bgpStateConsumer -> {
             final KeyedInstanceIdentifier<Rib, RibKey> ribId = bgpStateConsumer.getInstanceIdentifier();
@@ -142,13 +176,13 @@ public final class StateProviderImpl implements TransactionChainListener, AutoCl
         oldStats.forEach(ribId -> removeStoredOperationalState(ribId, wtx));
     }
 
-    private synchronized void removeStoredOperationalState(final String ribId, final WriteTransaction wtx) {
+    private synchronized void removeStoredOperationalState(final String ribId, final WriteOperations wtx) {
         final InstanceIdentifier<Bgp> bgpIID = this.instanceIdentifiersCache.remove(ribId);
         wtx.delete(LogicalDatastoreType.OPERATIONAL, bgpIID);
     }
 
     private synchronized void storeOperationalState(final BGPRibState bgpStateConsumer,
-            final List<BGPPeerState> peerStats, final String ribId, final WriteTransaction wtx) {
+            final List<BGPPeerState> peerStats, final String ribId, final WriteOperations wtx) {
         final Global global = GlobalUtil.buildGlobal(bgpStateConsumer, this.bgpTableTypeRegistry);
         final PeerGroups peerGroups = PeerGroupUtil.buildPeerGroups(peerStats);
         final Neighbors neighbors = NeighborUtil.buildNeighbors(peerStats, this.bgpTableTypeRegistry);
@@ -166,11 +200,13 @@ public final class StateProviderImpl implements TransactionChainListener, AutoCl
         wtx.mergeParentStructurePut(LogicalDatastoreType.OPERATIONAL, bgpIID, bgp);
     }
 
+    @Deactivate
+    @PreDestroy
     @Override
     public synchronized void close() {
         if (closed.compareAndSet(false, true)) {
             this.scheduleTask.cancel(true);
-            if (!this.instanceIdentifiersCache.keySet().isEmpty()) {
+            if (!this.instanceIdentifiersCache.isEmpty()) {
                 final WriteTransaction wTx = this.transactionChain.newWriteOnlyTransaction();
                 this.instanceIdentifiersCache.values()
                         .forEach(bgpIID -> wTx.delete(LogicalDatastoreType.OPERATIONAL, bgpIID));
diff --git a/bgp/openconfig-state/src/main/resources/OSGI-INF/blueprint/bgp-openconfig-state.xml b/bgp/openconfig-state/src/main/resources/OSGI-INF/blueprint/bgp-openconfig-state.xml
deleted file mode 100644 (file)
index bf963a8..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  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
--->
-<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
-           xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0">
-
-    <reference id="dataBroker" interface="org.opendaylight.mdsal.binding.api.DataBroker"/>
-    <reference id="bgpTableTypeRegistry" interface="org.opendaylight.protocol.bgp.openconfig.spi.BGPTableTypeRegistryConsumer"/>
-    <reference id="bgpStateProvider" interface="org.opendaylight.protocol.bgp.rib.spi.state.BGPStateConsumer"/>
-
-    <odl:clustered-app-config id="bgpStateConfig"
-                              binding-class="org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.state.config.rev161107.BgpStateConfig"/>
-
-    <bean id="bgpOpenconfigState" class="org.opendaylight.protocol.bgp.state.StateProviderImpl"
-          init-method="init" destroy-method="close">
-        <argument ref="dataBroker"/>
-        <argument>
-            <bean factory-ref="bgpStateConfig" factory-method="getTimer"/>
-        </argument>
-        <argument ref="bgpTableTypeRegistry"/>
-        <argument ref="bgpStateProvider"/>
-        <argument value="global-bgp"/>
-    </bean>
-</blueprint>
diff --git a/bgp/openconfig-state/src/main/yang/bgp-state-config.yang b/bgp/openconfig-state/src/main/yang/bgp-state-config.yang
deleted file mode 100644 (file)
index 80a71fe..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-// vi: set smarttab et sw=4 tabstop=4:
-module bgp-state-config {
-    yang-version 1;
-    namespace "urn:opendaylight:params:xml:ns:yang:bgp-state-config";
-    prefix bgp-state-config;
-
-    description
-        "This module contains the base YANG definitions for
-         BGP State Configuration.
-         Copyright (c)2016 Cisco Systems, Inc. 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";
-
-    revision "2016-11-07" {
-        description
-            "Initial revision.";
-    }
-
-    container bgp-state-config {
-        leaf config-name {
-            type string;
-            mandatory true;
-        }
-
-        leaf timer {
-            type uint16;
-            default 5;
-            units "seconds";
-        }
-    }
-}
\ No newline at end of file
index 1347c857b2f5f6c91cfe0f0f60589544b7ef5995..f2a3149e3b6e5639cf93abfb86cf0c1955310f66 100644 (file)
@@ -10,25 +10,26 @@ package org.opendaylight.protocol.bgp.state;
 import static org.junit.Assert.assertNull;
 import static org.mockito.Mockito.doReturn;
 
-import java.util.Collections;
+import java.util.List;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnitRunner;
 import org.opendaylight.protocol.bgp.rib.spi.state.BGPPeerState;
 
+@RunWith(MockitoJUnitRunner.StrictStubs.class)
 public class PeerGroupUtilTest {
     @Mock
     private BGPPeerState bgpPeerState;
 
     @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-        doReturn(null).when(this.bgpPeerState).getGroupId();
+    public void setUp() {
+        doReturn(null).when(bgpPeerState).getGroupId();
     }
 
     @Test
     public void testNoneGroup() {
-        assertNull(PeerGroupUtil.buildPeerGroups(Collections.singletonList(this.bgpPeerState)));
+        assertNull(PeerGroupUtil.buildPeerGroups(List.of(bgpPeerState)));
     }
 }
\ No newline at end of file
index bb7b3ee845081cb5b2c517be8c4010e2add1d858..42a62dc6e0f0f4e288b9c14d8ba314b9a799319c 100644 (file)
@@ -8,6 +8,8 @@
 package org.opendaylight.protocol.bgp.state;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -23,26 +25,26 @@ import static org.mockito.Mockito.verify;
 import static org.opendaylight.protocol.util.CheckUtil.checkNotPresentOperational;
 import static org.opendaylight.protocol.util.CheckUtil.readDataOperational;
 
-import com.google.common.collect.ImmutableList;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListeningExecutorService;
 import com.google.common.util.concurrent.MoreExecutors;
+import com.google.common.util.concurrent.Uninterruptibles;
 import java.math.BigDecimal;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.LongAdder;
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnitRunner;
 import org.opendaylight.infrautils.testutils.LogCapture;
 import org.opendaylight.infrautils.testutils.internal.RememberingLogger;
 import org.opendaylight.mdsal.binding.dom.adapter.test.AbstractDataBrokerTest;
@@ -142,6 +144,7 @@ import org.opendaylight.yangtools.yang.common.Uint32;
 import org.opendaylight.yangtools.yang.common.Uint64;
 import org.slf4j.LoggerFactory;
 
+@RunWith(MockitoJUnitRunner.StrictStubs.class)
 public class StateProviderImplTest extends AbstractDataBrokerTest {
     private final LongAdder totalPathsCounter = new LongAdder();
     private final LongAdder totalPrefixesCounter = new LongAdder();
@@ -157,7 +160,7 @@ public class StateProviderImplTest extends AbstractDataBrokerTest {
     private final AsNumber as = new AsNumber(Uint32.valueOf(72));
     private final BgpId bgpId = new BgpId("127.0.0.1");
     private final IpAddressNoZone neighborAddress = new IpAddressNoZone(new Ipv4AddressNoZone("127.0.0.2"));
-    private final List<Class<? extends BgpCapability>> supportedCap = Arrays.asList(ASN32.class, ROUTEREFRESH.class,
+    private final List<Class<? extends BgpCapability>> supportedCap = List.of(ASN32.class, ROUTEREFRESH.class,
             MPBGP.class, ADDPATHS.class, GRACEFULRESTART.class);
     @Mock
     private BGPStateConsumer stateCollector;
@@ -190,8 +193,6 @@ public class StateProviderImplTest extends AbstractDataBrokerTest {
 
     @Before
     public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
         doReturn(IPV4UNICAST.class).when(this.tableTypeRegistry).getAfiSafiType(eq(TABLES_KEY));
 
         doReturn(this.bgpRibStates).when(this.stateCollector).getRibStats();
@@ -211,9 +212,7 @@ public class StateProviderImplTest extends AbstractDataBrokerTest {
                 .when(this.bgpRibState).getPathCount(eq(TABLES_KEY));
         doAnswer(invocation -> this.totalPrefixesCounter.longValue())
                 .when(this.bgpRibState).getPrefixesCount(eq(TABLES_KEY));
-        doAnswer(invocation -> Collections.singletonMap(TABLES_KEY,
-            this.totalPrefixesCounter.longValue())).when(this.bgpRibState).getTablesPrefixesCount();
-        doAnswer(invocation -> Collections.singletonMap(TABLES_KEY,
+        doAnswer(invocation -> Map.of(TABLES_KEY,
             this.totalPathsCounter.longValue())).when(this.bgpRibState).getPathsCount();
 
         // Mock Peer
@@ -249,16 +248,14 @@ public class StateProviderImplTest extends AbstractDataBrokerTest {
         doReturn(1L).when(this.bgpErrorHandlingState).getErroneousUpdateReceivedCount();
 
         doReturn(this.bgpGracelfulRestartState).when(this.bgpPeerState).getBGPGracelfulRestart();
-        doReturn(true).when(this.bgpGracelfulRestartState).isGracefulRestartAdvertized(any());
-        doReturn(true).when(this.bgpGracelfulRestartState).isGracefulRestartReceived(any());
         doReturn(true).when(this.bgpGracelfulRestartState).isLocalRestarting();
         doReturn(true).when(this.bgpGracelfulRestartState).isPeerRestarting();
         doReturn(this.restartTime.toJava()).when(this.bgpGracelfulRestartState).getPeerRestartTime();
         doReturn(BgpAfiSafiGracefulRestartState.Mode.BILATERAL).when(this.bgpGracelfulRestartState).getMode();
 
         doReturn(this.bgpAfiSafiState).when(this.bgpPeerState).getBGPAfiSafiState();
-        doReturn(Collections.singleton(TABLES_KEY)).when(this.bgpAfiSafiState).getAfiSafisAdvertized();
-        doReturn(Collections.singleton(TABLES_KEY)).when(this.bgpAfiSafiState).getAfiSafisReceived();
+        doReturn(Set.of(TABLES_KEY)).when(this.bgpAfiSafiState).getAfiSafisAdvertized();
+        doReturn(Set.of(TABLES_KEY)).when(this.bgpAfiSafiState).getAfiSafisReceived();
         doReturn(1L).when(this.bgpAfiSafiState).getPrefixesInstalledCount(any());
         doReturn(2L).when(this.bgpAfiSafiState).getPrefixesReceivedCount(any());
         doReturn(1L).when(this.bgpAfiSafiState).getPrefixesSentCount(any());
@@ -293,100 +290,105 @@ public class StateProviderImplTest extends AbstractDataBrokerTest {
         doReturn(true).when(this.bgpRibState).isActive();
         doReturn(true).when(this.bgpPeerState).isActive();
 
-        final StateProviderImpl stateProvider = new StateProviderImpl(getDataBroker(), 1, this.tableTypeRegistry,
-            this.stateCollector, "global-bgp");
-        stateProvider.init();
-
-        final Global globalExpected = buildGlobalExpected(0);
-        this.bgpRibStates.add(this.bgpRibState);
-        readDataOperational(getDataBroker(), this.bgpInstanceIdentifier, bgpRib -> {
-            final Global global = bgpRib.getGlobal();
-            assertEquals(globalExpected, global);
-            return bgpRib;
-        });
-
-        this.totalPathsCounter.increment();
-        this.totalPrefixesCounter.increment();
-
-        final Global globalExpected2 = buildGlobalExpected(1);
-        readDataOperational(getDataBroker(), this.bgpInstanceIdentifier, bgpRib -> {
-            final Global global = bgpRib.getGlobal();
-            assertEquals(globalExpected2, global);
-            return bgpRib;
-        });
-
-        this.totalPathsCounter.decrement();
-        this.totalPrefixesCounter.decrement();
-
-        final Global globalExpected3 = buildGlobalExpected(0);
-        readDataOperational(getDataBroker(), this.bgpInstanceIdentifier, bgpRib -> {
-            final Global global = bgpRib.getGlobal();
-            assertEquals(globalExpected3, global);
-            Assert.assertNull(bgpRib.getNeighbors());
-            Assert.assertNull(bgpRib.getPeerGroups());
-            return bgpRib;
-        });
-
-        this.bgpPeerStates.add(this.bgpPeerState);
-        final PeerGroup peerGroupExpected = buildGroupExpected();
-
-        this.totalPathsCounter.increment();
-        this.totalPrefixesCounter.increment();
-
-        final AfiSafis expectedAfiSafis = buildAfiSafis();
-        final ErrorHandling expectedErrorHandling = buildErrorHandling();
-        final GracefulRestart expectedGracefulRestart = buildGracefulRestart();
-        final Transport expectedTransport = buildTransport();
-        final Timers expectedTimers = buildTimers();
-        final BgpNeighborStateAugmentation expectedBgpNeighborState = buildBgpNeighborStateAugmentation();
-
-        readDataOperational(getDataBroker(), this.bgpInstanceIdentifier, bgpRib -> {
-            final Neighbors neighbors = bgpRib.getNeighbors();
-            Assert.assertNotNull(neighbors);
-            assertEquals(peerGroupExpected, bgpRib.getPeerGroups().nonnullPeerGroup().values().iterator().next());
-            final Neighbor neighborResult = neighbors.nonnullNeighbor().values().iterator().next();
-            assertEquals(new IpAddress(neighborAddress.getIpv4AddressNoZone()), neighborResult.getNeighborAddress());
-            assertEquals(expectedAfiSafis, neighborResult.getAfiSafis());
-            assertEquals(expectedErrorHandling, neighborResult.getErrorHandling());
-            assertEquals(expectedGracefulRestart, neighborResult.getGracefulRestart());
-            assertEquals(expectedTransport, neighborResult.getTransport());
-            assertEquals(expectedTimers, neighborResult.getTimers());
-            final org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.neighbor.group
+        try (StateProviderImpl stateProvider =
+                // FIXME: use a properly-controlled executor service
+                new StateProviderImpl(getDataBroker(), 1, tableTypeRegistry, stateCollector, "global-bgp")) {
+
+            final Global globalExpected = buildGlobalExpected(0);
+            this.bgpRibStates.add(this.bgpRibState);
+            readDataOperational(getDataBroker(), this.bgpInstanceIdentifier, bgpRib -> {
+                final Global global = bgpRib.getGlobal();
+                assertEquals(globalExpected, global);
+                return bgpRib;
+            });
+
+            this.totalPathsCounter.increment();
+            this.totalPrefixesCounter.increment();
+
+            final Global globalExpected2 = buildGlobalExpected(1);
+            readDataOperational(getDataBroker(), this.bgpInstanceIdentifier, bgpRib -> {
+                final Global global = bgpRib.getGlobal();
+                assertEquals(globalExpected2, global);
+                return bgpRib;
+            });
+
+            this.totalPathsCounter.decrement();
+            this.totalPrefixesCounter.decrement();
+
+            final Global globalExpected3 = buildGlobalExpected(0);
+            readDataOperational(getDataBroker(), this.bgpInstanceIdentifier, bgpRib -> {
+                final Global global = bgpRib.getGlobal();
+                assertEquals(globalExpected3, global);
+                assertNull(bgpRib.getNeighbors());
+                assertNull(bgpRib.getPeerGroups());
+                return bgpRib;
+            });
+
+            this.bgpPeerStates.add(this.bgpPeerState);
+            final PeerGroup peerGroupExpected = buildGroupExpected();
+
+            this.totalPathsCounter.increment();
+            this.totalPrefixesCounter.increment();
+
+            final AfiSafis expectedAfiSafis = buildAfiSafis();
+            final ErrorHandling expectedErrorHandling = buildErrorHandling();
+            final GracefulRestart expectedGracefulRestart = buildGracefulRestart();
+            final Transport expectedTransport = buildTransport();
+            final Timers expectedTimers = buildTimers();
+            final BgpNeighborStateAugmentation expectedBgpNeighborState = buildBgpNeighborStateAugmentation();
+
+            readDataOperational(getDataBroker(), bgpInstanceIdentifier, bgpRib -> {
+                final Neighbors neighbors = bgpRib.getNeighbors();
+                assertNotNull(neighbors);
+                assertEquals(peerGroupExpected, bgpRib.getPeerGroups().nonnullPeerGroup().values().iterator().next());
+                final Neighbor neighborResult = neighbors.nonnullNeighbor().values().iterator().next();
+                assertEquals(new IpAddress(neighborAddress.getIpv4AddressNoZone()),
+                    neighborResult.getNeighborAddress());
+                assertEquals(expectedAfiSafis, neighborResult.getAfiSafis());
+                assertEquals(expectedErrorHandling, neighborResult.getErrorHandling());
+                assertEquals(expectedGracefulRestart, neighborResult.getGracefulRestart());
+                assertEquals(expectedTransport, neighborResult.getTransport());
+                assertEquals(expectedTimers, neighborResult.getTimers());
+                final org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.rev151009.bgp.neighbor.group
                     .State stateResult = neighborResult.getState();
-            assertEquals(expectedBgpNeighborState, stateResult.augmentation(BgpNeighborStateAugmentation.class));
-            assertEquals(BgpNeighborState.SessionState.ESTABLISHED, stateResult
+                assertEquals(expectedBgpNeighborState, stateResult.augmentation(BgpNeighborStateAugmentation.class));
+                assertEquals(BgpNeighborState.SessionState.ESTABLISHED, stateResult
                     .augmentation(NeighborStateAugmentation.class).getSessionState());
-            final List<Class<? extends BgpCapability>> supportedCapabilitiesResult = stateResult
+                final List<Class<? extends BgpCapability>> supportedCapabilitiesResult = stateResult
                     .augmentation(NeighborStateAugmentation.class).getSupportedCapabilities();
-            Assert.assertTrue(supportedCapabilitiesResult.containsAll(this.supportedCap));
-            return bgpRib;
-        });
+                assertTrue(supportedCapabilitiesResult.containsAll(this.supportedCap));
+                return bgpRib;
+            });
 
-        this.bgpRibStates.clear();
-        checkNotPresentOperational(getDataBroker(), this.bgpInstanceIdentifier);
-
-        stateProvider.close();
+            this.bgpRibStates.clear();
+            checkNotPresentOperational(getDataBroker(), this.bgpInstanceIdentifier);
+        }
     }
 
     @Test
     public void testInactiveStateProvider() throws Exception {
         doReturn(false).when(this.bgpRibState).isActive();
-        doReturn(false).when(this.bgpPeerState).isActive();
-
-        final StateProviderImpl stateProvider = new StateProviderImpl(getDataBroker(), 1, this.tableTypeRegistry,
-            this.stateCollector, "global-bgp");
-        stateProvider.init();
 
-        this.bgpRibStates.add(this.bgpRibState);
-        checkNotPresentOperational(getDataBroker(), this.bgpInstanceIdentifier);
-
-        this.bgpPeerStates.add(this.bgpPeerState);
-        checkNotPresentOperational(getDataBroker(), this.bgpInstanceIdentifier);
-
-        this.bgpRibStates.clear();
-        checkNotPresentOperational(getDataBroker(), this.bgpInstanceIdentifier);
-
-        stateProvider.close();
+        try (StateProviderImpl stateProvider =
+                new StateProviderImpl(getDataBroker(), 100, TimeUnit.MILLISECONDS, tableTypeRegistry, stateCollector,
+                    // FIXME: use a properly-controlled executor service ...
+                    "global-bgp", Executors.newScheduledThreadPool(1))) {
+
+            bgpRibStates.add(this.bgpRibState);
+            /// ... and trigger here
+            Uninterruptibles.sleepUninterruptibly(500, TimeUnit.MILLISECONDS);
+            checkNotPresentOperational(getDataBroker(), this.bgpInstanceIdentifier);
+
+            bgpPeerStates.add(this.bgpPeerState);
+            /// ... and trigger here
+            Uninterruptibles.sleepUninterruptibly(500, TimeUnit.MILLISECONDS);
+            checkNotPresentOperational(getDataBroker(), this.bgpInstanceIdentifier);
+
+            bgpRibStates.clear();
+            /// ... and trigger here
+            Uninterruptibles.sleepUninterruptibly(500, TimeUnit.MILLISECONDS);
+            checkNotPresentOperational(getDataBroker(), this.bgpInstanceIdentifier);
+        }
     }
 
     @Test
@@ -409,7 +411,6 @@ public class StateProviderImplTest extends AbstractDataBrokerTest {
         doNothing().when(mockScheduler).shutdown();
 
         DOMStoreTransactionChain mockTxChain = mock(DOMStoreTransactionChain.class);
-        doNothing().when(mockTxChain).close();
 
         Throwable mockCommitEx = new Exception("mock commit failure");
         doAnswer(invocation -> {
@@ -421,7 +422,6 @@ public class StateProviderImplTest extends AbstractDataBrokerTest {
                 DOMStoreWriteTransaction mockWriteTx = mock(DOMStoreReadWriteTransaction .class);
                 doNothing().when(mockWriteTx).write(any(), any());
                 doNothing().when(mockWriteTx).merge(any(), any());
-                doNothing().when(mockWriteTx).delete(any());
                 doReturn(mockCohort).when(mockWriteTx).ready();
                 return mockWriteTx;
             }).when(mockTxChain).newReadWriteTransaction();
@@ -429,14 +429,13 @@ public class StateProviderImplTest extends AbstractDataBrokerTest {
             return mockTxChain;
         }).doAnswer(invocation -> realOperStore.createTransactionChain()).when(spiedOperStore).createTransactionChain();
 
-        int timerInterval = 1;
-        try (StateProviderImpl stateProvider = new StateProviderImpl(getDataBroker(), timerInterval, tableTypeRegistry,
+        final int period = 100;
+        final TimeUnit unit = TimeUnit.MILLISECONDS;
+        try (StateProviderImpl stateProvider = new StateProviderImpl(getDataBroker(), period, unit, tableTypeRegistry,
                 stateCollector, "global-bgp", mockScheduler)) {
-            stateProvider.init();
 
             ArgumentCaptor<Runnable> timerTask = ArgumentCaptor.forClass(Runnable.class);
-            verify(mockScheduler).scheduleAtFixedRate(timerTask.capture(), eq(0L), eq((long)timerInterval),
-                    eq(TimeUnit.SECONDS));
+            verify(mockScheduler).scheduleAtFixedRate(timerTask.capture(), eq(0L), eq((long)period), eq(unit));
 
             timerTask.getValue().run();
 
@@ -449,7 +448,7 @@ public class StateProviderImplTest extends AbstractDataBrokerTest {
 
             timerTask.getValue().run();
 
-            ImmutableList<LogCapture> loggedErrors = RememberingLogger.getErrorLogCaptures();
+            List<LogCapture> loggedErrors = RememberingLogger.getErrorLogCaptures();
             assertTrue("Expected no logged ERRORs: " + loggedErrors, loggedErrors.isEmpty());
 
             verify(spiedOperStore, times(2)).createTransactionChain();