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.base.Function;
11 import com.google.common.util.concurrent.Futures;
12 import com.google.common.util.concurrent.ListenableFuture;
13 import io.netty.util.internal.ConcurrentSet;
14 import java.util.ArrayList;
15 import java.util.List;
17 import java.util.concurrent.atomic.AtomicBoolean;
18 import javax.annotation.Nonnull;
19 import javax.annotation.Nullable;
20 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider;
21 import org.opendaylight.openflowplugin.api.openflow.OFPContext;
22 import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
23 import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
24 import org.opendaylight.openflowplugin.api.openflow.device.DeviceInfo;
25 import org.opendaylight.openflowplugin.api.openflow.lifecycle.ContextChain;
26 import org.opendaylight.openflowplugin.api.openflow.lifecycle.ContextChainMastershipState;
27 import org.opendaylight.openflowplugin.api.openflow.lifecycle.ContextChainState;
28 import org.opendaylight.openflowplugin.api.openflow.lifecycle.ContextChainStateListener;
29 import org.opendaylight.openflowplugin.api.openflow.lifecycle.LifecycleService;
30 import org.opendaylight.openflowplugin.api.openflow.rpc.RpcContext;
31 import org.opendaylight.openflowplugin.api.openflow.statistics.StatisticsContext;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
35 public class ContextChainImpl implements ContextChain {
37 private static final Logger LOG = LoggerFactory.getLogger(ContextChainImpl.class);
39 private Set<OFPContext> contexts = new ConcurrentSet<>();
40 private StatisticsContext statisticsContext;
41 private DeviceContext deviceContext;
42 private RpcContext rpcContext;
43 private LifecycleService lifecycleService;
44 private DeviceInfo deviceInfo;
45 private ConnectionContext primaryConnection;
46 private Set<ConnectionContext> auxiliaryConnections = new ConcurrentSet<>();
48 private volatile ContextChainState contextChainState;
50 private AtomicBoolean masterStateOnDevice;
51 private AtomicBoolean initialGathering;
52 private AtomicBoolean initialSubmitting;
53 private AtomicBoolean registryFilling;
55 ContextChainImpl(final ConnectionContext connectionContext) {
56 this.primaryConnection = connectionContext;
57 this.contextChainState = ContextChainState.UNDEFINED;
58 this.masterStateOnDevice = new AtomicBoolean(false);
59 this.initialGathering = new AtomicBoolean(false);
60 this.initialSubmitting = new AtomicBoolean(false);
61 this.registryFilling = new AtomicBoolean(false);
62 this.deviceInfo = connectionContext.getDeviceInfo();
66 public <T extends OFPContext> void addContext(final T context) {
67 if (context instanceof StatisticsContext) {
68 this.statisticsContext = (StatisticsContext) context;
70 if (context instanceof DeviceContext) {
71 this.deviceContext = (DeviceContext) context;
73 if (context instanceof RpcContext) {
74 this.rpcContext = (RpcContext) context;
79 contexts.add(context);
83 public void addLifecycleService(final LifecycleService lifecycleService) {
84 this.lifecycleService = lifecycleService;
88 public ListenableFuture<Void> stopChain() {
89 //TODO: stopClusterServices change parameter
90 final List<ListenableFuture<Void>> futureList = new ArrayList<>();
91 futureList.add(statisticsContext.stopClusterServices());
92 futureList.add(rpcContext.stopClusterServices());
93 futureList.add(deviceContext.stopClusterServices());
95 return Futures.transform(Futures.successfulAsList(futureList), new Function<List<Void>, Void>() {
98 public Void apply(@Nullable List<Void> input) {
99 LOG.info("Closed clustering MASTER services for node {}", deviceContext.getDeviceInfo().getLOGValue());
105 private void unMasterMe() {
106 this.registryFilling.set(false);
107 this.initialSubmitting.set(false);
108 this.initialGathering.set(false);
109 this.masterStateOnDevice.set(false);
113 public void close() {
114 this.auxiliaryConnections.forEach(connectionContext -> connectionContext.closeConnection(false));
115 if (this.primaryConnection.getConnectionState() != ConnectionContext.CONNECTION_STATE.RIP) {
116 this.primaryConnection.closeConnection(true);
118 lifecycleService.close();
119 deviceContext.close();
121 statisticsContext.close();
125 public void makeContextChainStateSlave() {
127 changeState(ContextChainState.WORKING_SLAVE);
131 public ListenableFuture<Void> connectionDropped() {
132 if (this.contextChainState == ContextChainState.WORKING_MASTER) {
133 return this.stopChain();
136 return Futures.immediateFuture(null);
140 public void registerServices(final ClusterSingletonServiceProvider clusterSingletonServiceProvider) {
141 this.lifecycleService.registerService(
142 clusterSingletonServiceProvider,
147 public void makeDeviceSlave() {
149 this.lifecycleService.makeDeviceSlave(this.deviceContext);
153 public boolean isMastered(@Nonnull ContextChainMastershipState mastershipState) {
154 switch (mastershipState) {
156 LOG.debug("Device {}, initial submit OK.", deviceInfo.getLOGValue());
157 this.initialSubmitting.set(true);
159 case MASTER_ON_DEVICE:
160 LOG.debug("Device {}, master state OK.", deviceInfo.getLOGValue());
161 this.masterStateOnDevice.set(true);
163 case INITIAL_GATHERING:
164 LOG.debug("Device {}, initial gathering OK.", deviceInfo.getLOGValue());
165 this.initialGathering.set(true);
167 //Flow registry fill is not mandatory to work as a master
168 case INITIAL_FLOW_REGISTRY_FILL:
169 LOG.debug("Device {}, initial registry filling OK.", deviceInfo.getLOGValue());
170 this.registryFilling.set(true);
174 final boolean result =
175 this.initialGathering.get() &&
176 this.masterStateOnDevice.get() &&
177 this.initialSubmitting.get();
179 if (result && mastershipState != ContextChainMastershipState.CHECK) {
180 LOG.info("Device {} is able to work as master{}",
181 deviceInfo.getLOGValue(),
182 this.registryFilling.get() ? " WITHOUT flow registry !!!" : ".");
183 changeState(ContextChainState.WORKING_MASTER);
189 public boolean hasState() {
190 return contextChainState == ContextChainState.WORKING_MASTER
191 || contextChainState == ContextChainState.WORKING_SLAVE;
195 public boolean addAuxiliaryConnection(@Nonnull ConnectionContext connectionContext) {
196 if (this.primaryConnection.getConnectionState() != ConnectionContext.CONNECTION_STATE.RIP) {
197 this.auxiliaryConnections.add(connectionContext);
205 public boolean auxiliaryConnectionDropped(@Nonnull ConnectionContext connectionContext) {
206 if (this.auxiliaryConnections.isEmpty()) {
209 if (!this.auxiliaryConnections.contains(connectionContext)) {
212 this.auxiliaryConnections.remove(connectionContext);
216 private void changeState(final ContextChainState contextChainState) {
217 boolean propagate = this.contextChainState == ContextChainState.UNDEFINED;
218 this.contextChainState = contextChainState;
222 .filter(ContextChainStateListener.class::isInstance)
223 .map(ContextChainStateListener.class::cast)
224 .forEach(listener -> listener.onStateAcquired(contextChainState));