2 * Copyright (c) 2016 Pantheon Technologies s.r.o. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.openflowplugin.impl.lifecycle;
10 import com.google.common.annotations.VisibleForTesting;
11 import com.google.common.base.Verify;
12 import com.google.common.util.concurrent.FutureCallback;
13 import com.google.common.util.concurrent.Futures;
14 import com.google.common.util.concurrent.ListenableFuture;
15 import io.netty.util.HashedWheelTimer;
16 import java.util.Collections;
17 import java.util.HashMap;
19 import java.util.Objects;
20 import java.util.Optional;
21 import java.util.concurrent.ExecutorService;
22 import java.util.concurrent.TimeUnit;
23 import java.util.concurrent.TimeoutException;
24 import javax.annotation.Nonnull;
25 import javax.annotation.Nullable;
26 import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipChange;
27 import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipListenerRegistration;
28 import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService;
29 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
30 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider;
31 import org.opendaylight.openflowplugin.api.openflow.OFPManager;
32 import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
33 import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionStatus;
34 import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
35 import org.opendaylight.openflowplugin.api.openflow.device.DeviceInfo;
36 import org.opendaylight.openflowplugin.api.openflow.device.DeviceManager;
37 import org.opendaylight.openflowplugin.api.openflow.lifecycle.ContextChain;
38 import org.opendaylight.openflowplugin.api.openflow.lifecycle.ContextChainHolder;
39 import org.opendaylight.openflowplugin.api.openflow.lifecycle.ContextChainMastershipState;
40 import org.opendaylight.openflowplugin.api.openflow.lifecycle.LifecycleService;
41 import org.opendaylight.openflowplugin.api.openflow.rpc.RpcContext;
42 import org.opendaylight.openflowplugin.api.openflow.rpc.RpcManager;
43 import org.opendaylight.openflowplugin.api.openflow.statistics.StatisticsContext;
44 import org.opendaylight.openflowplugin.api.openflow.statistics.StatisticsManager;
45 import org.opendaylight.openflowplugin.impl.util.DeviceStateUtil;
46 import org.opendaylight.openflowplugin.impl.util.ItemScheduler;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
50 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
51 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
55 public class ContextChainHolderImpl implements ContextChainHolder {
56 private static final Logger LOG = LoggerFactory.getLogger(ContextChainHolderImpl.class);
58 private static final String CONTEXT_CREATED_FOR_CONNECTION = " context created for connection: {}";
59 private static final long CHECK_ROLE_MASTER_TIMEOUT = 20000L;
60 private static final long CHECK_ROLE_MASTER_TOLERANCE = CHECK_ROLE_MASTER_TIMEOUT / 2;
61 private static final long REMOVE_DEVICE_FROM_DS_TIMEOUT = 5000L;
62 private static final String ASYNC_SERVICE_ENTITY_TYPE = "org.opendaylight.mdsal.AsyncServiceCloseEntityType";
64 private final Map<DeviceInfo, ContextChain> contextChainMap = Collections.synchronizedMap(new HashMap<>());
65 private final EntityOwnershipListenerRegistration eosListenerRegistration;
66 private final ClusterSingletonServiceProvider singletonServiceProvider;
67 private final ItemScheduler<DeviceInfo, ContextChain> scheduler;
68 private final ExecutorService executorService;
69 private DeviceManager deviceManager;
70 private RpcManager rpcManager;
71 private StatisticsManager statisticsManager;
73 public ContextChainHolderImpl(final HashedWheelTimer timer,
74 final ExecutorService executorService,
75 final ClusterSingletonServiceProvider singletonServiceProvider,
76 final EntityOwnershipService entityOwnershipService) {
77 this.singletonServiceProvider = singletonServiceProvider;
78 this.executorService = executorService;
79 this.eosListenerRegistration = Verify.verifyNotNull(entityOwnershipService.registerListener
80 (ASYNC_SERVICE_ENTITY_TYPE, this));
82 this.scheduler = new ItemScheduler<>(
84 CHECK_ROLE_MASTER_TIMEOUT,
85 CHECK_ROLE_MASTER_TOLERANCE,
86 ContextChain::makeDeviceSlave);
90 public <T extends OFPManager> void addManager(final T manager) {
91 if (Objects.isNull(deviceManager) && manager instanceof DeviceManager) {
92 LOG.trace("Context chain holder: Device manager OK.");
93 deviceManager = (DeviceManager) manager;
94 } else if (Objects.isNull(rpcManager) && manager instanceof RpcManager) {
95 LOG.trace("Context chain holder: RPC manager OK.");
96 rpcManager = (RpcManager) manager;
97 } else if (Objects.isNull(statisticsManager) && manager instanceof StatisticsManager) {
98 LOG.trace("Context chain holder: Statistics manager OK.");
99 statisticsManager = (StatisticsManager) manager;
104 public ContextChain createContextChain(final ConnectionContext connectionContext) {
105 final DeviceInfo deviceInfo = connectionContext.getDeviceInfo();
106 final String deviceInfoLOGValue = deviceInfo.getLOGValue();
107 final ContextChain contextChain = new ContextChainImpl(connectionContext);
109 if (LOG.isDebugEnabled()) {
110 LOG.debug("Context chain" + CONTEXT_CREATED_FOR_CONNECTION, deviceInfoLOGValue);
113 final LifecycleService lifecycleService = new LifecycleServiceImpl(this, executorService);
114 lifecycleService.registerDeviceRemovedHandler(deviceManager);
115 lifecycleService.registerDeviceRemovedHandler(rpcManager);
116 lifecycleService.registerDeviceRemovedHandler(statisticsManager);
118 if (LOG.isDebugEnabled()) {
119 LOG.debug("Lifecycle services" + CONTEXT_CREATED_FOR_CONNECTION, deviceInfoLOGValue);
122 final DeviceContext deviceContext = deviceManager.createContext(connectionContext);
124 if (LOG.isDebugEnabled()) {
125 LOG.debug("Device" + CONTEXT_CREATED_FOR_CONNECTION, deviceInfoLOGValue);
128 final RpcContext rpcContext = rpcManager.createContext(connectionContext.getDeviceInfo(), deviceContext);
130 if (LOG.isDebugEnabled()) {
131 LOG.debug("RPC" + CONTEXT_CREATED_FOR_CONNECTION, deviceInfoLOGValue);
134 final StatisticsContext statisticsContext = statisticsManager.createContext(deviceContext);
136 if (LOG.isDebugEnabled()) {
137 LOG.debug("Statistics" + CONTEXT_CREATED_FOR_CONNECTION, deviceInfoLOGValue);
140 deviceContext.setLifecycleInitializationPhaseHandler(statisticsContext);
141 statisticsContext.setLifecycleInitializationPhaseHandler(rpcContext);
142 statisticsContext.setInitialSubmitHandler(deviceContext);
144 contextChain.addLifecycleService(lifecycleService);
145 contextChain.addContext(deviceContext);
146 contextChain.addContext(rpcContext);
147 contextChain.addContext(statisticsContext);
149 LOG.info("Starting timer for setting SLAVE role on node {} if no role will be set in {}s.",
150 deviceInfo.getLOGValue(), CHECK_ROLE_MASTER_TIMEOUT / 1000L);
151 scheduler.add(deviceInfo, contextChain);
152 scheduler.startIfNotRunning();
153 deviceContext.onPublished();
154 contextChain.registerServices(singletonServiceProvider);
159 public synchronized void destroyContextChain(final DeviceInfo deviceInfo) {
160 Optional.ofNullable(contextChainMap.remove(deviceInfo)).ifPresent(contextChain -> {
161 deviceManager.sendNodeRemovedNotification(deviceInfo.getNodeInstanceIdentifier());
162 contextChain.close();
167 public ConnectionStatus deviceConnected(final ConnectionContext connectionContext) throws Exception {
168 final DeviceInfo deviceInfo = connectionContext.getDeviceInfo();
169 final ContextChain contextChain = contextChainMap.get(deviceInfo);
170 LOG.info("Device {} connected.", deviceInfo.getLOGValue());
172 if (contextChain != null) {
173 if (contextChain.addAuxiliaryConnection(connectionContext)) {
174 LOG.info("An auxiliary connection was added to device: {}", deviceInfo.getLOGValue());
175 return ConnectionStatus.MAY_CONTINUE;
177 LOG.warn("Device {} already connected. Closing all connection to the device.", deviceInfo.getLOGValue());
178 destroyContextChain(deviceInfo);
179 return ConnectionStatus.ALREADY_CONNECTED;
182 if (LOG.isDebugEnabled()) {
183 LOG.debug("No context chain found for device: {}, creating new.", deviceInfo.getLOGValue());
185 contextChainMap.put(deviceInfo, createContextChain(connectionContext));
188 return ConnectionStatus.MAY_CONTINUE;
192 public void onNotAbleToStartMastership(final DeviceInfo deviceInfo, @Nonnull final String reason, final boolean mandatory) {
193 LOG.warn("Not able to set MASTER role on device {}, reason: {}", deviceInfo.getLOGValue(), reason);
199 Optional.ofNullable(contextChainMap.get(deviceInfo)).ifPresent(contextChain -> {
200 LOG.warn("This mastering is mandatory, destroying context chain and closing connection for device {}.", deviceInfo.getLOGValue());
201 addDestroyChainCallback(contextChain.stopChain(), deviceInfo);
206 public void onMasterRoleAcquired(final DeviceInfo deviceInfo, @Nonnull final ContextChainMastershipState mastershipState) {
207 scheduler.remove(deviceInfo);
209 Optional.ofNullable(contextChainMap.get(deviceInfo)).ifPresent(contextChain -> {
210 if (contextChain.isMastered(mastershipState)) {
211 LOG.info("Role MASTER was granted to device {}", deviceInfo.getLOGValue());
212 deviceManager.sendNodeAddedNotification(deviceInfo.getNodeInstanceIdentifier());
218 public void onSlaveRoleAcquired(final DeviceInfo deviceInfo) {
219 Optional.ofNullable(contextChainMap.get(deviceInfo)).ifPresent(ContextChain::makeContextChainStateSlave);
223 public void onSlaveRoleNotAcquired(final DeviceInfo deviceInfo) {
224 Optional.ofNullable(contextChainMap.get(deviceInfo)).ifPresent(contextChain -> destroyContextChain(deviceInfo));
228 public void onDeviceDisconnected(final ConnectionContext connectionContext) {
229 final DeviceInfo deviceInfo = connectionContext.getDeviceInfo();
231 if (Objects.isNull(deviceInfo)) {
235 Optional.ofNullable(contextChainMap.get(deviceInfo)).ifPresent(contextChain -> {
236 if (contextChain.auxiliaryConnectionDropped(connectionContext)) {
237 LOG.info("Auxiliary connection from device {} disconnected.", deviceInfo.getLOGValue());
239 LOG.info("Device {} disconnected.", deviceInfo.getLOGValue());
240 addDestroyChainCallback(contextChain.connectionDropped(), deviceInfo);
246 boolean checkAllManagers() {
247 return Objects.nonNull(deviceManager) && Objects.nonNull(rpcManager) && Objects.nonNull(statisticsManager);
251 public void close() throws Exception {
254 contextChainMap.forEach((deviceInfo, contextChain) -> {
255 if (contextChain.isMastered(ContextChainMastershipState.CHECK)) {
256 addDestroyChainCallback(contextChain.stopChain(), deviceInfo);
258 destroyContextChain(deviceInfo);
262 contextChainMap.clear();
263 eosListenerRegistration.close();
267 public void ownershipChanged(EntityOwnershipChange entityOwnershipChange) {
268 if (entityOwnershipChange.hasOwner()) {
272 final String entityName = getEntityNameFromOwnershipChange(entityOwnershipChange);
274 if (Objects.nonNull(entityName)) {
275 if (LOG.isDebugEnabled()) {
276 LOG.debug("Entity {} has no owner", entityName);
279 final NodeId nodeId = new NodeId(entityName);
282 final KeyedInstanceIdentifier<Node, NodeKey> nodeInstanceIdentifier =
283 DeviceStateUtil.createNodeInstanceIdentifier(nodeId);
285 deviceManager.sendNodeRemovedNotification(nodeInstanceIdentifier);
287 LOG.info("Removing device {} from operational DS", nodeId);
289 .removeDeviceFromOperationalDS(nodeInstanceIdentifier)
290 .checkedGet(REMOVE_DEVICE_FROM_DS_TIMEOUT, TimeUnit.MILLISECONDS);
291 } catch (TimeoutException | TransactionCommitFailedException e) {
292 LOG.info("Not able to remove device {} from operational DS. Probably removed by another cluster node.",
298 private String getEntityNameFromOwnershipChange(final EntityOwnershipChange entityOwnershipChange) {
299 final YangInstanceIdentifier.NodeIdentifierWithPredicates lastIdArgument =
300 (YangInstanceIdentifier.NodeIdentifierWithPredicates) entityOwnershipChange
303 .getLastPathArgument();
305 return lastIdArgument
313 private void addDestroyChainCallback(final ListenableFuture<Void> future, final DeviceInfo deviceInfo) {
314 scheduler.remove(deviceInfo);
316 Futures.addCallback(future, new FutureCallback<Void>() {
318 public void onSuccess(@Nullable final Void aVoid) {
319 destroyContextChain(deviceInfo);
323 public void onFailure(@Nonnull final Throwable throwable) {
324 destroyContextChain(deviceInfo);