From: Jozef Gloncak Date: Thu, 16 Jun 2016 13:42:45 +0000 (+0200) Subject: Clustering - common infrastructure. X-Git-Tag: release/boron~40 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F44%2F40444%2F17;p=lispflowmapping.git Clustering - common infrastructure. Common class for clustering operations for each module. Change-Id: If401eba45155e07846f025340b6499969213e7bb Signed-off-by: Jozef Gloncak --- diff --git a/features/pom.xml b/features/pom.xml index 9b7afcf71..f0a31d5ac 100644 --- a/features/pom.xml +++ b/features/pom.xml @@ -89,6 +89,11 @@ and is available at http://www.eclipse.org/legal/epl-v10.html xml runtime + + ${project.groupId} + mappingservice.clustering + ${project.version} + ${project.groupId} mappingservice.dsbackend diff --git a/features/src/main/features/features.xml b/features/src/main/features/features.xml index 314fc6e78..0cfbbfde1 100644 --- a/features/src/main/features/features.xml +++ b/features/src/main/features/features.xml @@ -31,6 +31,7 @@ odl-mdsal-broker odl-lispflowmapping-models odl-lispflowmapping-inmemorydb + mvn:org.opendaylight.lispflowmapping/mappingservice.clustering/{{VERSION}} mvn:org.opendaylight.lispflowmapping/mappingservice.dsbackend/{{VERSION}} mvn:org.opendaylight.lispflowmapping/mappingservice.mapcache/{{VERSION}} mvn:org.opendaylight.lispflowmapping/mappingservice.implementation/{{VERSION}} @@ -52,6 +53,7 @@ odl-mdsal-broker odl-lispflowmapping-models odl-lispflowmapping-inmemorydb + mvn:org.opendaylight.lispflowmapping/mappingservice.clustering/{{VERSION}} mvn:org.opendaylight.lispflowmapping/mappingservice.dsbackend/{{VERSION}} mvn:org.opendaylight.lispflowmapping/mappingservice.mapcache/{{VERSION}} mvn:org.opendaylight.lispflowmapping/mappingservice.southbound/{{VERSION}} diff --git a/mappingservice/clustering/pom.xml b/mappingservice/clustering/pom.xml new file mode 100644 index 000000000..4ebfe9c7a --- /dev/null +++ b/mappingservice/clustering/pom.xml @@ -0,0 +1,65 @@ + + + + + 4.0.0 + + + org.opendaylight.lispflowmapping + mappingservice-parent + 1.4.0-SNAPSHOT + + + mappingservice.clustering + bundle + Mapping Service Clustering (mappingservice.clustering) + + + + org.slf4j + slf4j-api + + + org.opendaylight.controller + sal-common-api + + + + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + true + checkstyle.violationSeverity=error + + + + + + + + ${odl.site.url}/${project.groupId}/${stream}/${project.artifactId}/ + + + + opendaylight-site + ${nexus.site.url}/${project.artifactId}/ + + + diff --git a/mappingservice/clustering/src/main/java/org/opendaylight/lispflowmapping/clustering/ClusterNodeModulSwitcherImpl.java b/mappingservice/clustering/src/main/java/org/opendaylight/lispflowmapping/clustering/ClusterNodeModulSwitcherImpl.java new file mode 100644 index 000000000..c3b806887 --- /dev/null +++ b/mappingservice/clustering/src/main/java/org/opendaylight/lispflowmapping/clustering/ClusterNodeModulSwitcherImpl.java @@ -0,0 +1,85 @@ +/* + * 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.lispflowmapping.clustering; + +import static org.opendaylight.lispflowmapping.clustering.util.ClusteringUtil.LISPFLOWMAPPING_ENTITY_NAME; +import static org.opendaylight.lispflowmapping.clustering.util.ClusteringUtil.LISPFLOWMAPPING_ENTITY_TYPE; + +import com.google.common.base.Optional; +import org.opendaylight.controller.md.sal.common.api.clustering.CandidateAlreadyRegisteredException; +import org.opendaylight.controller.md.sal.common.api.clustering.Entity; +import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipChange; +import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipListener; +import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService; +import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipState; +import org.opendaylight.lispflowmapping.clustering.api.ClusterNodeModuleSwitcher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Class responsible for turning on|off module in node cluster. + */ +public class ClusterNodeModulSwitcherImpl implements EntityOwnershipListener { + + private static final Logger LOG = LoggerFactory.getLogger(ClusterNodeModulSwitcherImpl.class); + private final EntityOwnershipService entityOwnershipService; + private final Entity entity; + private boolean moduleIsRunning = true; + private ClusterNodeModuleSwitcher module; + + public ClusterNodeModulSwitcherImpl(final EntityOwnershipService entityOwnershipService) { + this.entityOwnershipService = entityOwnershipService; + this.entityOwnershipService.registerListener(LISPFLOWMAPPING_ENTITY_NAME, this); + entity = new Entity(LISPFLOWMAPPING_ENTITY_NAME, LISPFLOWMAPPING_ENTITY_TYPE); + try { + this.entityOwnershipService.registerCandidate(entity); + } catch (CandidateAlreadyRegisteredException e) { + LOG.debug("Candidate already registered. Trace: {}", e); + } + } + + @Override + public void ownershipChanged(final EntityOwnershipChange entityOwnershipChange) { + LOG.debug("Entity ownership change message received."); + switchModuleState(entityOwnershipChange.isOwner()); + } + + public void switchModuleByEntityOwnership() { + switchModuleState(isMaster()); + } + + private void switchModuleState(final boolean isOwner) { + if (module != null) { + if (!isOwner && moduleIsRunning) { + module.stopModule(); + moduleIsRunning = false; + LOG.debug("Module {} was stopped.", module.getClass().getName()); + } else if (isOwner && !moduleIsRunning) { + module.startModule(); + moduleIsRunning = true; + LOG.debug("Module {} was restarted.", module.getClass().getName()); + } + } else { + LOG.debug("Module wasn't initialized yet."); + } + } + + public boolean isMaster() { + final Optional ownershipState = entityOwnershipService.getOwnershipState(entity); + if (ownershipState.isPresent()) { + return ownershipState.get().isOwner(); + } else { + LOG.debug("Ownership state information wasn't present in entity ownership service."); + } + return false; + } + + public void setModule(final ClusterNodeModuleSwitcher module) { + this.module = module; + } +} diff --git a/mappingservice/clustering/src/main/java/org/opendaylight/lispflowmapping/clustering/api/ClusterNodeModuleSwitcher.java b/mappingservice/clustering/src/main/java/org/opendaylight/lispflowmapping/clustering/api/ClusterNodeModuleSwitcher.java new file mode 100644 index 000000000..0d20ffef0 --- /dev/null +++ b/mappingservice/clustering/src/main/java/org/opendaylight/lispflowmapping/clustering/api/ClusterNodeModuleSwitcher.java @@ -0,0 +1,20 @@ +/* + * 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.lispflowmapping.clustering.api; + +/** + * Interface specify operations necessary to turn on|off module in node cluster. + */ +public interface ClusterNodeModuleSwitcher { + + void startModule(); + + void stopModule(); + +} diff --git a/mappingservice/clustering/src/main/java/org/opendaylight/lispflowmapping/clustering/util/ClusteringUtil.java b/mappingservice/clustering/src/main/java/org/opendaylight/lispflowmapping/clustering/util/ClusteringUtil.java new file mode 100644 index 000000000..0031252c9 --- /dev/null +++ b/mappingservice/clustering/src/main/java/org/opendaylight/lispflowmapping/clustering/util/ClusteringUtil.java @@ -0,0 +1,20 @@ +/* + * 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.lispflowmapping.clustering.util; + +public final class ClusteringUtil { + + public static final String LISPFLOWMAPPING_ENTITY_NAME = "lispflowmapping"; + public static final String LISPFLOWMAPPING_ENTITY_TYPE = "application"; + + private ClusteringUtil() { + throw new UnsupportedOperationException(); + } + +} diff --git a/mappingservice/clustering/src/main/test/org/opendaylight/lispflowmapping/clustering/ClusterNodeModulSwitcherImplTest.java b/mappingservice/clustering/src/main/test/org/opendaylight/lispflowmapping/clustering/ClusterNodeModulSwitcherImplTest.java new file mode 100644 index 000000000..c8d39fb96 --- /dev/null +++ b/mappingservice/clustering/src/main/test/org/opendaylight/lispflowmapping/clustering/ClusterNodeModulSwitcherImplTest.java @@ -0,0 +1,92 @@ +/* + * 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.lispflowmapping.clustering; + +import static org.opendaylight.lispflowmapping.clustering.util.ClusteringUtil.LISPFLOWMAPPING_ENTITY_NAME; +import static org.opendaylight.lispflowmapping.clustering.util.ClusteringUtil.LISPFLOWMAPPING_ENTITY_TYPE; + +import com.google.common.base.Optional; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Matchers; +import org.mockito.Mockito; +import org.opendaylight.controller.md.sal.common.api.clustering.CandidateAlreadyRegisteredException; +import org.opendaylight.controller.md.sal.common.api.clustering.Entity; +import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipChange; +import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService; +import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipState; +import org.opendaylight.lispflowmapping.clustering.api.ClusterNodeModuleSwitcher; + +public class ClusterNodeModulSwitcherImplTest { + + private EntityOwnershipService entityOwnershipServiceMocked; + private ClusterNodeModulSwitcherImpl clusterNodeModulSwitcherImpl; + private ClusterNodeModuleSwitcher module; + private Entity entity = new Entity(LISPFLOWMAPPING_ENTITY_NAME, LISPFLOWMAPPING_ENTITY_TYPE); + + @Before + public void init() { + entityOwnershipServiceMocked = Mockito.mock(EntityOwnershipService.class); + clusterNodeModulSwitcherImpl = new ClusterNodeModulSwitcherImpl(entityOwnershipServiceMocked); + module = Mockito.mock(ClusterNodeModuleSwitcher.class); + } + + @Test + public void constructorCallTest() throws CandidateAlreadyRegisteredException { + Mockito.verify(entityOwnershipServiceMocked).registerListener(Matchers.eq(LISPFLOWMAPPING_ENTITY_NAME), + Matchers.any(ClusterNodeModulSwitcherImpl.class)); + Mockito.verify(entityOwnershipServiceMocked).registerCandidate(Matchers.eq(entity)); + } + + @Test + public void ownershipChangedTest() { + //is not owner + ownershipChanged(false); + Mockito.verify(module).stopModule(); + //is owner + ownershipChanged(true); + Mockito.verify(module).startModule(); + } + + @Test + public void isMasterTest_OptionalAbsent() { + Mockito.when(entityOwnershipServiceMocked.getOwnershipState(Matchers.eq(entity))).thenReturn(Optional + .absent + ()); + Assert.assertFalse(clusterNodeModulSwitcherImpl.isMaster()); + } + + @Test + public void isMasterTest_True() { + Mockito.when(entityOwnershipServiceMocked.getOwnershipState(Matchers.eq(entity))).thenReturn(Optional + .of(new EntityOwnershipState(true, true))); + Assert.assertTrue(clusterNodeModulSwitcherImpl.isMaster()); + } + + @Test + public void isMasterTest_False() { + Mockito.when(entityOwnershipServiceMocked.getOwnershipState(Matchers.eq(entity))).thenReturn(Optional + .of(new EntityOwnershipState(false, true))); + Assert.assertFalse(clusterNodeModulSwitcherImpl.isMaster()); + } + + @Test + public void switchModuleByEntityOwnershipTest() { + Mockito.when(entityOwnershipServiceMocked.getOwnershipState(Matchers.eq(entity))).thenReturn(Optional + .of(new EntityOwnershipState(false, true))); + clusterNodeModulSwitcherImpl.switchModuleByEntityOwnership(); + } + + private void ownershipChanged(boolean isOwner) { + final EntityOwnershipChange entityOwnershipChangeMock = Mockito.mock(EntityOwnershipChange.class); + clusterNodeModulSwitcherImpl.setModule(module); + Mockito.when(entityOwnershipChangeMock.isOwner()).thenReturn(isOwner); + clusterNodeModulSwitcherImpl.ownershipChanged(entityOwnershipChangeMock); + } +} diff --git a/mappingservice/implementation/src/main/java/org/opendaylight/lispflowmapping/implementation/mdsal/AbstractDataListener.java b/mappingservice/implementation/src/main/java/org/opendaylight/lispflowmapping/implementation/mdsal/AbstractDataListener.java index 97ac5550e..39e319e3e 100644 --- a/mappingservice/implementation/src/main/java/org/opendaylight/lispflowmapping/implementation/mdsal/AbstractDataListener.java +++ b/mappingservice/implementation/src/main/java/org/opendaylight/lispflowmapping/implementation/mdsal/AbstractDataListener.java @@ -7,6 +7,7 @@ */ package org.opendaylight.lispflowmapping.implementation.mdsal; +import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener; import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier; @@ -19,7 +20,7 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; * The superclass for the different MD-SAL data change event listeners. * */ -public abstract class AbstractDataListener implements DataTreeChangeListener { +public abstract class AbstractDataListener implements ClusteredDataTreeChangeListener { private DataBroker broker; private InstanceIdentifier path; private ListenerRegistration> registration; diff --git a/mappingservice/pom.xml b/mappingservice/pom.xml index ab1b19cfc..f24a1e3ee 100644 --- a/mappingservice/pom.xml +++ b/mappingservice/pom.xml @@ -18,6 +18,7 @@ lisp-proto api + clustering inmemorydb dsbackend mapcache diff --git a/mappingservice/southbound/src/main/java/org/opendaylight/lispflowmapping/southbound/lisp/AuthenticationKeyDataListener.java b/mappingservice/southbound/src/main/java/org/opendaylight/lispflowmapping/southbound/lisp/AuthenticationKeyDataListener.java index d66ed11ca..9496005b1 100644 --- a/mappingservice/southbound/src/main/java/org/opendaylight/lispflowmapping/southbound/lisp/AuthenticationKeyDataListener.java +++ b/mappingservice/southbound/src/main/java/org/opendaylight/lispflowmapping/southbound/lisp/AuthenticationKeyDataListener.java @@ -9,6 +9,7 @@ package org.opendaylight.lispflowmapping.southbound.lisp; import java.util.Collection; +import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener; 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.DataObjectModification.ModificationType; @@ -32,7 +33,7 @@ import org.slf4j.LoggerFactory; * DataListener for all AuthenticationKey modification events. * */ -public class AuthenticationKeyDataListener implements DataTreeChangeListener { +public class AuthenticationKeyDataListener implements ClusteredDataTreeChangeListener { private static final Logger LOG = LoggerFactory.getLogger(AuthenticationKeyDataListener.class); private final SimpleMapCache smc;