From 801877cf5cd60120ef487e0cfee3c05860403ef7 Mon Sep 17 00:00:00 2001 From: Michal Polkorab Date: Thu, 14 Jan 2016 06:19:51 +0100 Subject: [PATCH] Bug 4957 RoleContext updated with initialization - introduced blocking waiting for device role - we probably need blocking call to EntityOwnershipService for Netty thread Change-Id: I4a89f07b9594975dae77219af09a9909f4a39dcd Signed-off-by: Jozef Bacigal --- .../api/openflow/role/RoleContext.java | 21 ++++ .../impl/OpenFlowPluginProviderImpl.java | 13 +-- .../impl/device/DeviceContextImpl.java | 28 +++--- .../impl/device/DeviceManagerImpl.java | 43 ++++---- .../impl/role/RoleContextImpl.java | 53 +++++++--- .../impl/role/RoleManagerImpl.java | 98 +++++++++++++------ .../impl/util/DeviceInitializationUtils.java | 4 +- .../impl/device/DeviceContextImplTest.java | 16 +-- 8 files changed, 180 insertions(+), 96 deletions(-) diff --git a/openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/role/RoleContext.java b/openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/role/RoleContext.java index 102a7e6257..f1016eacec 100644 --- a/openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/role/RoleContext.java +++ b/openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/role/RoleContext.java @@ -8,14 +8,35 @@ package org.opendaylight.openflowplugin.api.openflow.role; import com.google.common.util.concurrent.FutureCallback; +import java.util.concurrent.Future; +import org.opendaylight.controller.md.sal.common.api.clustering.CandidateAlreadyRegisteredException; import org.opendaylight.openflowplugin.api.openflow.device.RequestContextStack; import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceContextClosedHandler; +import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole; /** * Created by kramesha on 9/12/15. */ public interface RoleContext extends RoleChangeListener, DeviceContextClosedHandler, RequestContextStack { + /** + * @deprecated do not use it + * @param futureCallback - future + */ + @Deprecated void facilitateRoleChange(FutureCallback futureCallback); + /** + * Initialization method is responsible for a registration of + * {@link org.opendaylight.controller.md.sal.common.api.clustering.Entity} + * and listen for notification from service. {@link Future} returned object is used primary + * for new connection initialization phase where we have to wait for actual Role. + * The {@link Future} has to be canceled if device is in disconnected state or when + * {@link org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService} returns + * {@link org.opendaylight.controller.md.sal.common.api.clustering.CandidateAlreadyRegisteredException} + * @return InitializationFuture for to know where first initial Election is done and we know role. + * @throws CandidateAlreadyRegisteredException - we have registered Entity so drop actual connection + */ + Future initialization() throws CandidateAlreadyRegisteredException; + } diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/OpenFlowPluginProviderImpl.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/OpenFlowPluginProviderImpl.java index 7ddde03430..4908331a52 100644 --- a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/OpenFlowPluginProviderImpl.java +++ b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/OpenFlowPluginProviderImpl.java @@ -120,15 +120,17 @@ public class OpenFlowPluginProviderImpl implements OpenFlowPluginProvider, OpenF }); } + @Override public boolean isSwitchFeaturesMandatory() { return switchFeaturesMandatory; } @Override - public void setEntityOwnershipService(EntityOwnershipService entityOwnershipService) { + public void setEntityOwnershipService(final EntityOwnershipService entityOwnershipService) { this.entityOwnershipService = entityOwnershipService; } + @Override public void setSwitchFeaturesMandatory(final boolean switchFeaturesMandatory) { this.switchFeaturesMandatory = switchFeaturesMandatory; } @@ -170,8 +172,7 @@ public class OpenFlowPluginProviderImpl implements OpenFlowPluginProvider, OpenF deviceManager = new DeviceManagerImpl(dataBroker, messageIntelligenceAgency, switchFeaturesMandatory, globalNotificationQuota); ((ExtensionConverterProviderKeeper) deviceManager).setExtensionConverterProvider(extensionConverterManager); - - roleManager = new RoleManagerImpl(rpcProviderRegistry, entityOwnershipService); + roleManager = new RoleManagerImpl(rpcProviderRegistry, entityOwnershipService, switchFeaturesMandatory); statisticsManager = new StatisticsManagerImpl(rpcProviderRegistry, isStatisticsPollingOff); rpcManager = new RpcManagerImpl(rpcProviderRegistry, rpcRequestsQuota); @@ -194,12 +195,12 @@ public class OpenFlowPluginProviderImpl implements OpenFlowPluginProvider, OpenF } private static void registerMXBean(final MessageIntelligenceAgency messageIntelligenceAgency) { - MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); try { - String pathToMxBean = String.format("%s:type=%s", + final String pathToMxBean = String.format("%s:type=%s", MessageIntelligenceAgencyMXBean.class.getPackage().getName(), MessageIntelligenceAgencyMXBean.class.getSimpleName()); - ObjectName name = new ObjectName(pathToMxBean); + final ObjectName name = new ObjectName(pathToMxBean); mbs.registerMBean(messageIntelligenceAgency, name); } catch (MalformedObjectNameException | NotCompliantMBeanException diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/device/DeviceContextImpl.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/device/DeviceContextImpl.java index 1decf7ee3d..683bb12c66 100644 --- a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/device/DeviceContextImpl.java +++ b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/device/DeviceContextImpl.java @@ -7,13 +7,7 @@ */ package org.opendaylight.openflowplugin.impl.device; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Preconditions; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import io.netty.util.HashedWheelTimer; -import io.netty.util.Timeout; +import javax.annotation.Nonnull; import java.math.BigInteger; import java.util.Collection; import java.util.HashMap; @@ -21,7 +15,14 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import javax.annotation.Nonnull; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import io.netty.util.HashedWheelTimer; +import io.netty.util.Timeout; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService; import org.opendaylight.controller.md.sal.binding.api.NotificationService; @@ -118,7 +119,7 @@ public class DeviceContextImpl implements DeviceContext, ExtensionConverterProvi private final DataBroker dataBroker; private final HashedWheelTimer hashedWheelTimer; private final Map auxiliaryConnectionContexts; - private final TransactionChainManager transactionChainManager; + private TransactionChainManager transactionChainManager; private final DeviceFlowRegistry deviceFlowRegistry; private final DeviceGroupRegistry deviceGroupRegistry; private final DeviceMeterRegistry deviceMeterRegistry; @@ -147,14 +148,13 @@ public class DeviceContextImpl implements DeviceContext, ExtensionConverterProvi @Nonnull final HashedWheelTimer hashedWheelTimer, @Nonnull final MessageSpy _messageSpy, @Nonnull final OutboundQueueProvider outboundQueueProvider, - @Nonnull final TranslatorLibrary translatorLibrary, - @Nonnull final TransactionChainManager transactionChainManager) { + @Nonnull final TranslatorLibrary translatorLibrary) { this.primaryConnectionContext = Preconditions.checkNotNull(primaryConnectionContext); this.deviceState = Preconditions.checkNotNull(deviceState); this.dataBroker = Preconditions.checkNotNull(dataBroker); this.hashedWheelTimer = Preconditions.checkNotNull(hashedWheelTimer); this.outboundQueueProvider = Preconditions.checkNotNull(outboundQueueProvider); - this.transactionChainManager = Preconditions.checkNotNull(transactionChainManager); + primaryConnectionContext.setDeviceDisconnectedHandler(DeviceContextImpl.this); auxiliaryConnectionContexts = new HashMap<>(); deviceFlowRegistry = new DeviceFlowRegistryImpl(); deviceGroupRegistry = new DeviceGroupRegistryImpl(); @@ -180,6 +180,10 @@ public class DeviceContextImpl implements DeviceContext, ExtensionConverterProvi itemLifeCycleSourceRegistry.registerLifeCycleSource(flowLifeCycleKeeper); } + void setTransactionChainManager(final TransactionChainManager txChainManager) { + this.transactionChainManager = Preconditions.checkNotNull(txChainManager); + } + /** * This method is called from {@link DeviceManagerImpl} only. So we could say "posthandshake process finish" * and we are able to set a scheduler for an automatic transaction submitting by time (0,5sec). diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/device/DeviceManagerImpl.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/device/DeviceManagerImpl.java index a38920d534..b5a99013b7 100644 --- a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/device/DeviceManagerImpl.java +++ b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/device/DeviceManagerImpl.java @@ -119,7 +119,7 @@ public class DeviceManagerImpl implements DeviceManager, ExtensionConverterProvi //if role = slave try { ((DeviceContextImpl) deviceContext).cancelTransaction(); - } catch (Exception e) { + } catch (final Exception e) { //TODO: how can we avoid it. pingpong does not have cancel LOG.debug("Expected Exception: Cancel Txn exception thrown for slaves", e); } @@ -140,31 +140,14 @@ public class DeviceManagerImpl implements DeviceManager, ExtensionConverterProvi @Override public void deviceConnected(@CheckForNull final ConnectionContext connectionContext) { Preconditions.checkArgument(connectionContext != null); - - ReadyForNewTransactionChainHandler readyForNewTransactionChainHandler = new ReadyForNewTransactionChainHandlerImpl(this, connectionContext); - DeviceTransactionChainManagerProvider.TransactionChainManagerRegistration transactionChainManagerRegistration = deviceTransactionChainManagerProvider.provideTransactionChainManager(connectionContext); - TransactionChainManager transactionChainManager = transactionChainManagerRegistration.getTransactionChainManager(); - - if (transactionChainManagerRegistration.ownedByInvokingConnectionContext()) { - //this actually is new registration for currently processed connection context - initializeDeviceContext(connectionContext, transactionChainManager); - } - else if (TransactionChainManager.TransactionChainManagerStatus.WORKING.equals(transactionChainManager.getTransactionChainManagerStatus())) { - //this means there already exists connection described by same NodeId and it is not current connection contexts' registration - LOG.info("In deviceConnected, ownedByInvokingConnectionContext is false and TransactionChainManagerStatus.WORKING. Closing connection to device to start again."); - connectionContext.closeConnection(false); - } - else if (!transactionChainManager.attemptToRegisterHandler(readyForNewTransactionChainHandler)) { - //previous connection is shutting down, we will try to register handler listening on new transaction chain ready - // new connection wil be closed if handler registration fails - LOG.info("In deviceConnected, ownedByInvokingConnectionContext is false, TransactionChainManagerStatus is not shutting down or readyForNewTransactionChainHandler is null. " + - "Closing connection to device to start again."); - connectionContext.closeConnection(false); + try { + initializeDeviceContext(connectionContext); + } catch (Exception e) { + LOG.warn("Exception during initialization phase.", e); } } - private void initializeDeviceContext(final ConnectionContext connectionContext, - final TransactionChainManager transactionChainManager) { + private void initializeDeviceContext(final ConnectionContext connectionContext) throws Exception{ LOG.info("Initializing New Connection DeviceContext for node:{}", connectionContext.getNodeId()); // Cache this for clarity final ConnectionAdapter connectionAdapter = connectionContext.getConnectionAdapter(); @@ -184,8 +167,20 @@ public class DeviceManagerImpl implements DeviceManager, ExtensionConverterProvi final DeviceState deviceState = new DeviceStateImpl(connectionContext.getFeatures(), nodeId); final DeviceContext deviceContext = new DeviceContextImpl(connectionContext, deviceState, dataBroker, - hashedWheelTimer, messageIntelligenceAgency, outboundQueueProvider, translatorLibrary, transactionChainManager); + hashedWheelTimer, messageIntelligenceAgency, outboundQueueProvider, translatorLibrary); + deviceContext.addDeviceContextClosedHandler(this); + // We would like to crete/register TxChainManager after + final DeviceTransactionChainManagerProvider.TransactionChainManagerRegistration txChainManagerReg = deviceTransactionChainManagerProvider + .provideTransactionChainManager(connectionContext); + if (txChainManagerReg.ownedByInvokingConnectionContext()) { + //this actually is new registration for currently processed connection context + ((DeviceContextImpl) deviceContext).setTransactionChainManager(txChainManagerReg.getTransactionChainManager()); + } else { + LOG.info("In deviceConnected {}, ownedByInvokingConnectionContext is false", connectionContext.getNodeId()); + deviceContext.close(); + return; + } ((ExtensionConverterProviderKeeper) deviceContext).setExtensionConverterProvider(extensionConverterProvider); deviceContext.setNotificationService(notificationService); deviceContext.setNotificationPublishService(notificationPublishService); diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/role/RoleContextImpl.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/role/RoleContextImpl.java index 06305e1879..1cfb39bc8e 100644 --- a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/role/RoleContextImpl.java +++ b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/role/RoleContextImpl.java @@ -11,6 +11,7 @@ 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.JdkFutureAdapters; +import com.google.common.util.concurrent.SettableFuture; import java.util.concurrent.Future; import javax.annotation.Nullable; import org.opendaylight.controller.md.sal.common.api.clustering.CandidateAlreadyRegisteredException; @@ -41,18 +42,19 @@ import org.slf4j.LoggerFactory; public class RoleContextImpl implements RoleContext { private static final Logger LOG = LoggerFactory.getLogger(RoleContextImpl.class); - private EntityOwnershipService entityOwnershipService; + private final EntityOwnershipService entityOwnershipService; private EntityOwnershipCandidateRegistration entityOwnershipCandidateRegistration; private final RpcProviderRegistry rpcProviderRegistry; - private DeviceContext deviceContext; - private Entity entity; - private OpenflowOwnershipListener openflowOwnershipListener; + private final DeviceContext deviceContext; + private final Entity entity; + private final OpenflowOwnershipListener openflowOwnershipListener; private SalRoleService salRoleService; private FutureCallback roleChangeCallback; + private final SettableFuture initRoleChangeFuture; - public RoleContextImpl(DeviceContext deviceContext, RpcProviderRegistry rpcProviderRegistry, - EntityOwnershipService entityOwnershipService, OpenflowOwnershipListener openflowOwnershipListener) { + public RoleContextImpl(final DeviceContext deviceContext, final RpcProviderRegistry rpcProviderRegistry, + final EntityOwnershipService entityOwnershipService, final OpenflowOwnershipListener openflowOwnershipListener) { this.entityOwnershipService = entityOwnershipService; this.rpcProviderRegistry = rpcProviderRegistry; this.deviceContext = deviceContext; @@ -61,12 +63,21 @@ public class RoleContextImpl implements RoleContext { this.openflowOwnershipListener = openflowOwnershipListener; salRoleService = new SalRoleServiceImpl(this, deviceContext); - //make a call to entity ownership service and listen for notifications from the service - requestOpenflowEntityOwnership(); + initRoleChangeFuture = SettableFuture.create(); } @Override - public void facilitateRoleChange(FutureCallback roleChangeCallback) { + public Future initialization() throws CandidateAlreadyRegisteredException { + LOG.debug("Initialization requestOpenflowEntityOwnership for entity {}", entity); + openflowOwnershipListener.registerRoleChangeListener(this); + entityOwnershipCandidateRegistration = entityOwnershipService.registerCandidate(entity); + LOG.info("RoleContextImpl : Candidate registered with ownership service for device :{}", deviceContext + .getPrimaryConnectionContext().getNodeId().getValue()); + return initRoleChangeFuture; + } + + @Override + public void facilitateRoleChange(final FutureCallback roleChangeCallback) { this.roleChangeCallback = roleChangeCallback; if (!isDeviceConnected()) { throw new IllegalStateException( @@ -83,7 +94,7 @@ public class RoleContextImpl implements RoleContext { // The role change listener must be registered after registering a candidate openflowOwnershipListener.registerRoleChangeListener(this); LOG.info("RoleContextImpl : Candidate registered with ownership service for device :{}", deviceContext.getPrimaryConnectionContext().getNodeId().getValue()); - } catch (CandidateAlreadyRegisteredException e) { + } catch (final CandidateAlreadyRegisteredException e) { // we can log and move for this error, as listener is present and role changes will be served. LOG.error("Candidate - Entity already registered with Openflow candidate ", entity, e ); } @@ -91,14 +102,24 @@ public class RoleContextImpl implements RoleContext { @Override public void onRoleChanged(final OfpRole oldRole, final OfpRole newRole) { + LOG.trace("onRoleChanged method call for Entity {}", entity); if (!isDeviceConnected()) { // this can happen as after the disconnect, we still get a last messsage from EntityOwnershipService. LOG.info("Device {} is disconnected from this node. Hence not attempting a role change.", deviceContext.getPrimaryConnectionContext().getNodeId()); + if (!initRoleChangeFuture.isDone()) { + LOG.debug("RoleChange is not valid for initialization Entity {} anymore - Device is disconnected", entity); + initRoleChangeFuture.cancel(true); + } return; } + if (!initRoleChangeFuture.isDone()) { + LOG.debug("Initialization Role for entity {} is chosed {}", entity, newRole); + initRoleChangeFuture.set(newRole); + } + LOG.debug("Role change received from ownership listener from {} to {} for device:{}", oldRole, newRole, deviceContext.getPrimaryConnectionContext().getNodeId()); @@ -107,11 +128,11 @@ public class RoleContextImpl implements RoleContext { .setNode(new NodeRef(deviceContext.getDeviceState().getNodeInstanceIdentifier())) .build(); - Future> setRoleOutputFuture = salRoleService.setRole(setRoleInput); + final Future> setRoleOutputFuture = salRoleService.setRole(setRoleInput); Futures.addCallback(JdkFutureAdapters.listenInPoolThread(setRoleOutputFuture), new FutureCallback>() { @Override - public void onSuccess(RpcResult setRoleOutputRpcResult) { + public void onSuccess(final RpcResult setRoleOutputRpcResult) { LOG.debug("Rolechange {} successful made on switch :{}", newRole, deviceContext.getPrimaryConnectionContext().getNodeId()); deviceContext.getDeviceState().setRole(newRole); @@ -121,7 +142,7 @@ public class RoleContextImpl implements RoleContext { } @Override - public void onFailure(Throwable throwable) { + public void onFailure(final Throwable throwable) { LOG.error("Error in setRole {} for device {} ", newRole, deviceContext.getPrimaryConnectionContext().getNodeId(), throwable); if (roleChangeCallback != null) { @@ -140,11 +161,11 @@ public class RoleContextImpl implements RoleContext { } @Override - public void onDeviceContextClosed(DeviceContext deviceContext) { + public void onDeviceContextClosed(final DeviceContext deviceContext) { try { LOG.debug("onDeviceContextClosed called"); this.close(); - } catch (Exception e) { + } catch (final Exception e) { LOG.error("Exception in onDeviceContextClosed of RoleContext", e); } } @@ -177,7 +198,7 @@ public class RoleContextImpl implements RoleContext { } @VisibleForTesting - public void setSalRoleService(SalRoleService salRoleService) { + public void setSalRoleService(final SalRoleService salRoleService) { this.salRoleService = salRoleService; } } diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/role/RoleManagerImpl.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/role/RoleManagerImpl.java index 78cb8c7e6f..0dbbd63a49 100644 --- a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/role/RoleManagerImpl.java +++ b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/role/RoleManagerImpl.java @@ -7,17 +7,29 @@ */ package org.opendaylight.openflowplugin.impl.role; -import com.google.common.util.concurrent.FutureCallback; +import javax.annotation.CheckForNull; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import javax.annotation.CheckForNull; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import org.opendaylight.controller.md.sal.common.api.clustering.CandidateAlreadyRegisteredException; import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService; +import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipState; import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry; import org.opendaylight.openflowplugin.api.OFConstants; import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext; import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceInitializationPhaseHandler; import org.opendaylight.openflowplugin.api.openflow.role.RoleContext; import org.opendaylight.openflowplugin.api.openflow.role.RoleManager; +import org.opendaylight.openflowplugin.impl.util.DeviceInitializationUtils; +import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,21 +43,24 @@ public class RoleManagerImpl implements RoleManager { private static final Logger LOG = LoggerFactory.getLogger(RoleManagerImpl.class); private DeviceInitializationPhaseHandler deviceInitializationPhaseHandler; - private EntityOwnershipService entityOwnershipService; + private final EntityOwnershipService entityOwnershipService; private final RpcProviderRegistry rpcProviderRegistry; private final ConcurrentHashMap contexts = new ConcurrentHashMap<>(); private final OpenflowOwnershipListener openflowOwnershipListener; + private final boolean switchFeaturesMandatory; - public RoleManagerImpl(RpcProviderRegistry rpcProviderRegistry, EntityOwnershipService entityOwnershipService) { - this.entityOwnershipService = entityOwnershipService; - this.rpcProviderRegistry = rpcProviderRegistry; + public RoleManagerImpl(final RpcProviderRegistry rpcProviderRegistry, + final EntityOwnershipService entityOwnershipService, final boolean switchFeaturesMandatory) { + this.entityOwnershipService = Preconditions.checkNotNull(entityOwnershipService); + this.rpcProviderRegistry = Preconditions.checkNotNull(rpcProviderRegistry); + this.switchFeaturesMandatory = switchFeaturesMandatory; this.openflowOwnershipListener = new OpenflowOwnershipListener(entityOwnershipService); LOG.debug("Registering OpenflowOwnershipListener listening to all entity ownership changes"); openflowOwnershipListener.init(); } @Override - public void setDeviceInitializationPhaseHandler(DeviceInitializationPhaseHandler handler) { + public void setDeviceInitializationPhaseHandler(final DeviceInitializationPhaseHandler handler) { deviceInitializationPhaseHandler = handler; } @@ -58,39 +73,64 @@ public class RoleManagerImpl implements RoleManager { return; } - RoleContext roleContext = new RoleContextImpl(deviceContext, rpcProviderRegistry, entityOwnershipService, openflowOwnershipListener); + final RoleContext roleContext = new RoleContextImpl(deviceContext, rpcProviderRegistry, entityOwnershipService, openflowOwnershipListener); contexts.put(deviceContext, roleContext); - LOG.debug("Created role context"); - // if the device context gets closed (mostly on connection close), we would need to cleanup deviceContext.addDeviceContextClosedHandler(roleContext); - - roleContext.facilitateRoleChange(new FutureCallback() { - @Override - public void onSuccess(Boolean aBoolean) { - LOG.debug("roleChangeFuture success for device:{}. Moving to StatisticsManager", deviceContext.getDeviceState().getNodeId()); - deviceInitializationPhaseHandler.onDeviceContextLevelUp(deviceContext); - } - - @Override - public void onFailure(Throwable throwable) { - LOG.error("RoleChange on device {} was not successful after several attempts. " + - "Closing the device Context, reconnect the device and start over", - deviceContext.getPrimaryConnectionContext().getNodeId().getValue(), throwable); + OfpRole role = null; + try { + role = roleContext.initialization().get(5, TimeUnit.SECONDS); + } catch (InterruptedException | ExecutionException | TimeoutException | CandidateAlreadyRegisteredException e) { + LOG.warn("Unexpected exception by DeviceConection {}. Connection has to close.", deviceContext.getDeviceState().getNodeId(), e); + final Optional entityOwnershipStateOptional = entityOwnershipService.getOwnershipState(roleContext.getEntity()); + if (entityOwnershipStateOptional.isPresent()) { + role = entityOwnershipStateOptional.get().isOwner() ? OfpRole.BECOMEMASTER : OfpRole.BECOMESLAVE; + } else { try { deviceContext.close(); - } catch (Exception e) { - LOG.warn("Error closing device context for device:{}", - deviceContext.getPrimaryConnectionContext().getNodeId().getValue(), e); + } catch (Exception e1) { + LOG.warn("Exception during device context close. ", e); } + return; } - }); + } + if (OfpRole.BECOMEMASTER.equals(role)) { + final ListenableFuture initNodeFuture = DeviceInitializationUtils.initializeNodeInformation(deviceContext, switchFeaturesMandatory); + Futures.addCallback(initNodeFuture, new FutureCallback() { + @Override + public void onSuccess(final Void result) { + LOG.trace("Node {} was initialized", deviceContext.getDeviceState().getNodeId()); + getRoleContextLevelUp(deviceContext); + } + + @Override + public void onFailure(final Throwable t) { + LOG.warn("Node {} Initialization fail", deviceContext.getDeviceState().getNodeId(), t); + try { + deviceContext.close(); + } catch (Exception e) { + LOG.warn("Exception during device context close. ", e); + } + } + }); + } else { + getRoleContextLevelUp(deviceContext); + } + + } + + void getRoleContextLevelUp(final DeviceContext deviceContext) { + LOG.debug("Created role context for node {}", deviceContext.getDeviceState().getNodeId()); + LOG.debug("roleChangeFuture success for device:{}. Moving to StatisticsManager", deviceContext.getDeviceState().getNodeId()); + deviceInitializationPhaseHandler.onDeviceContextLevelUp(deviceContext); } @Override public void close() throws Exception { - for (Map.Entry roleContextEntry : contexts.entrySet()) { - roleContextEntry.getValue().close(); + for (final Map.Entry roleContextEntry : contexts.entrySet()) { + if (roleContextEntry.getValue() != null) { + roleContextEntry.getValue().close(); + } } this.openflowOwnershipListener.close(); } diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/util/DeviceInitializationUtils.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/util/DeviceInitializationUtils.java index 784ea0a460..09310a4e39 100644 --- a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/util/DeviceInitializationUtils.java +++ b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/util/DeviceInitializationUtils.java @@ -98,7 +98,7 @@ public class DeviceInitializationUtils { * @param switchFeaturesMandatory * @return future - recommended to have blocking call for this future */ - public static Future initializeNodeInformation(final DeviceContext deviceContext, final boolean switchFeaturesMandatory) { + public static ListenableFuture initializeNodeInformation(final DeviceContext deviceContext, final boolean switchFeaturesMandatory) { Preconditions.checkArgument(deviceContext != null); final DeviceState deviceState = Preconditions.checkNotNull(deviceContext.getDeviceState()); final ConnectionContext connectionContext = Preconditions.checkNotNull(deviceContext.getPrimaryConnectionContext()); @@ -165,7 +165,7 @@ public class DeviceInitializationUtils { LOG.trace("Device capabilities gathering future failed."); LOG.trace("more info in exploration failure..", t); LOG.debug("All init data for node {} was not submited correctly - connection has to go down.", deviceState.getNodeId()); - returnFuture.cancel(true); + returnFuture.setException(t); } }); return returnFuture; diff --git a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/device/DeviceContextImplTest.java b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/device/DeviceContextImplTest.java index 34ee686fd8..71d16b6c88 100644 --- a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/device/DeviceContextImplTest.java +++ b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/device/DeviceContextImplTest.java @@ -16,6 +16,10 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import java.math.BigInteger; +import java.net.InetSocketAddress; +import java.util.concurrent.atomic.AtomicLong; + import com.google.common.base.Optional; import com.google.common.collect.Lists; import com.google.common.util.concurrent.CheckedFuture; @@ -102,9 +106,6 @@ import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier; import org.opendaylight.yangtools.yang.common.RpcResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.math.BigInteger; -import java.net.InetSocketAddress; -import java.util.concurrent.atomic.AtomicLong; @RunWith(MockitoJUnitRunner.class) public class DeviceContextImplTest { @@ -205,7 +206,8 @@ public class DeviceContextImplTest { org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FlowRemoved.class.getName())))) .thenReturn(messageTranslatorFlowRemoved); - deviceContext = new DeviceContextImpl(connectionContext, deviceState, dataBroker, timer, messageIntelligenceAgency, outboundQueueProvider, translatorLibrary, txChainManager); + deviceContext = new DeviceContextImpl(connectionContext, deviceState, dataBroker, timer, messageIntelligenceAgency, outboundQueueProvider, translatorLibrary); + deviceContext.setTransactionChainManager(txChainManager); xid = new Xid(atomicLong.incrementAndGet()); xidMulti = new Xid(atomicLong.incrementAndGet()); @@ -213,17 +215,17 @@ public class DeviceContextImplTest { @Test(expected = NullPointerException.class) public void testDeviceContextImplConstructorNullDataBroker() throws Exception { - new DeviceContextImpl(connectionContext, deviceState, null, timer, messageIntelligenceAgency, outboundQueueProvider, translatorLibrary, txChainManager).close(); + new DeviceContextImpl(connectionContext, deviceState, null, timer, messageIntelligenceAgency, outboundQueueProvider, translatorLibrary).close(); } @Test(expected = NullPointerException.class) public void testDeviceContextImplConstructorNullDeviceState() throws Exception { - new DeviceContextImpl(connectionContext, null, dataBroker, timer, messageIntelligenceAgency, outboundQueueProvider, translatorLibrary, txChainManager).close(); + new DeviceContextImpl(connectionContext, null, dataBroker, timer, messageIntelligenceAgency, outboundQueueProvider, translatorLibrary).close(); } @Test(expected = NullPointerException.class) public void testDeviceContextImplConstructorNullTimer() throws Exception { - new DeviceContextImpl(null, deviceState, dataBroker, null, messageIntelligenceAgency, outboundQueueProvider, translatorLibrary, txChainManager).close(); + new DeviceContextImpl(null, deviceState, dataBroker, null, messageIntelligenceAgency, outboundQueueProvider, translatorLibrary).close(); } @Test -- 2.36.6