/* * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.controller.cluster.datastore.entityownership; import static java.util.Objects.requireNonNull; import static org.junit.Assert.assertTrue; import static org.opendaylight.controller.cluster.datastore.entityownership.DistributedEntityOwnershipService.ENTITY_OWNERSHIP_SHARD_NAME; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.opendaylight.controller.cluster.datastore.AbstractDataStore; import org.opendaylight.controller.cluster.datastore.DatastoreContext; import org.opendaylight.controller.cluster.datastore.MemberNode; import org.opendaylight.controller.cluster.datastore.entityownership.selectionstrategy.EntityOwnerSelectionStrategyConfig; import org.opendaylight.controller.md.cluster.datastore.model.SchemaContextHelper; import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipService; import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonService; import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider; import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegistration; import org.opendaylight.mdsal.singleton.common.api.ServiceGroupIdentifier; import org.opendaylight.mdsal.singleton.dom.impl.DOMClusterSingletonServiceProviderImpl; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Simple integration test to check if the Entity Ownership Service implementation can support the Cluster Singleton * Service. * * @author Robert Varga */ public class SingletonServiceIntegrationTest { private static final class Service implements ClusterSingletonService { private final ClusterSingletonServiceProvider provider; private final ServiceGroupIdentifier identifier; private ClusterSingletonServiceRegistration reg; private long startCount; private long stopCount; Service(final ClusterSingletonServiceProvider provider, final String identifier) { this.provider = requireNonNull(provider); this.identifier = ServiceGroupIdentifier.create(identifier); } @Override public ServiceGroupIdentifier getIdentifier() { return identifier; } @Override public ListenableFuture closeServiceInstance() { LOG.debug("{} stopping", identifier); stopCount++; start(); return Futures.immediateFuture(null); } @SuppressWarnings("checkstyle:illegalCatch") @Override public void instantiateServiceInstance() { LOG.debug("{} starting", identifier); startCount++; try { reg.close(); } catch (Exception e) { throw new RuntimeException(e); } } void start() { reg = provider.registerClusterSingletonService(this); } long getStartCount() { return startCount; } long getStopCount() { return stopCount; } } private static final Logger LOG = LoggerFactory.getLogger(SingletonServiceIntegrationTest.class); private static final String MODULE_SHARDS_CONFIG = "module-shards-default.conf"; private static final SchemaContext SCHEMA_CONTEXT = SchemaContextHelper.entityOwners(); private static final long TEST_TIME_SECONDS = 10; private final DatastoreContext.Builder leaderDatastoreContextBuilder = DatastoreContext.newBuilder().shardHeartbeatIntervalInMillis(100).shardElectionTimeoutFactor(5) .shardIsolatedLeaderCheckIntervalInMillis(1000000); private final DatastoreContext.Builder followerDatastoreContextBuilder = DatastoreContext.newBuilder().shardHeartbeatIntervalInMillis(100).shardElectionTimeoutFactor(10000); private final List memberNodes = new ArrayList<>(); private ClusterSingletonServiceProvider leaderSingleton; private ClusterSingletonServiceProvider follower1Singleton; private ClusterSingletonServiceProvider follower2Singleton; private static DistributedEntityOwnershipService newOwnershipService(final AbstractDataStore datastore) { return DistributedEntityOwnershipService.start(datastore.getActorContext(), EntityOwnerSelectionStrategyConfig.newBuilder().build()); } @Before public void setup() throws Exception { final String name = "SingletonServiceIntegrationTest"; MemberNode leaderNode = MemberNode.builder(memberNodes).akkaConfig("Member1").testName(name) .moduleShardsConfig(MODULE_SHARDS_CONFIG).schemaContext(SCHEMA_CONTEXT).createOperDatastore(false) .datastoreContextBuilder(leaderDatastoreContextBuilder).build(); MemberNode follower1Node = MemberNode.builder(memberNodes).akkaConfig("Member2").testName(name) .moduleShardsConfig(MODULE_SHARDS_CONFIG).schemaContext(SCHEMA_CONTEXT).createOperDatastore(false) .datastoreContextBuilder(followerDatastoreContextBuilder).build(); MemberNode follower2Node = MemberNode.builder(memberNodes).akkaConfig("Member3").testName(name) .moduleShardsConfig(MODULE_SHARDS_CONFIG).schemaContext(SCHEMA_CONTEXT).createOperDatastore(false) .datastoreContextBuilder(followerDatastoreContextBuilder).build(); AbstractDataStore leaderDistributedDataStore = leaderNode.configDataStore(); leaderDistributedDataStore.waitTillReady(); follower1Node.configDataStore().waitTillReady(); follower2Node.configDataStore().waitTillReady(); final DOMEntityOwnershipService leaderEntityOwnershipService = newOwnershipService(leaderDistributedDataStore); final DOMEntityOwnershipService follower1EntityOwnershipService = newOwnershipService( follower1Node.configDataStore()); final DOMEntityOwnershipService follower2EntityOwnershipService = newOwnershipService( follower2Node.configDataStore()); leaderNode.kit().waitUntilLeader(leaderNode.configDataStore().getActorContext(), ENTITY_OWNERSHIP_SHARD_NAME); DOMClusterSingletonServiceProviderImpl impl = new DOMClusterSingletonServiceProviderImpl( leaderEntityOwnershipService); impl.initializeProvider(); leaderSingleton = impl; impl = new DOMClusterSingletonServiceProviderImpl(follower1EntityOwnershipService); impl.initializeProvider(); follower1Singleton = impl; impl = new DOMClusterSingletonServiceProviderImpl(follower2EntityOwnershipService); impl.initializeProvider(); follower2Singleton = impl; } @After public void tearDown() throws Exception { follower2Singleton.close(); follower1Singleton.close(); leaderSingleton.close(); for (MemberNode m : Lists.reverse(memberNodes)) { m.cleanup(); } memberNodes.clear(); } @Test public void testSingletonServiceIntegration() throws InterruptedException { List services = ImmutableList.of(new Service(leaderSingleton, "leader"), new Service(follower1Singleton, "follower1"), new Service(follower2Singleton, "follower2")); services.forEach(Service::start); LOG.info("Waiting {} seconds...", TEST_TIME_SECONDS); Thread.sleep(TimeUnit.SECONDS.toMillis(TEST_TIME_SECONDS)); long stops = 0; long starts = 0; for (Service service : services) { starts += service.getStartCount(); stops += service.getStopCount(); } LOG.info("Services saw {} starts and {} stops in {} seconds", starts, stops, TEST_TIME_SECONDS); assertTrue(starts > 0); assertTrue(stops <= starts); } }