Merge "Add Nicira extension support for matching IPv6 Src/Dst"
[openflowplugin.git] / openflowplugin-impl / src / main / java / org / opendaylight / openflowplugin / impl / lifecycle / ContextChainImpl.java
1 /*
2  * Copyright (c) 2016 Pantheon Technologies s.r.o. and others. All rights reserved.
3  *
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
7  */
8 package org.opendaylight.openflowplugin.impl.lifecycle;
9
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;
16 import java.util.Set;
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;
34
35 public class ContextChainImpl implements ContextChain {
36
37     private static final Logger LOG = LoggerFactory.getLogger(ContextChainImpl.class);
38
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<>();
47
48     private volatile ContextChainState contextChainState;
49
50     private AtomicBoolean masterStateOnDevice;
51     private AtomicBoolean initialGathering;
52     private AtomicBoolean initialSubmitting;
53     private AtomicBoolean registryFilling;
54
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();
63     }
64
65     @Override
66     public <T extends OFPContext> void addContext(final T context) {
67         if (context instanceof StatisticsContext) {
68             this.statisticsContext = (StatisticsContext) context;
69         } else {
70             if (context instanceof DeviceContext) {
71                 this.deviceContext = (DeviceContext) context;
72             } else {
73                 if (context instanceof RpcContext) {
74                     this.rpcContext = (RpcContext) context;
75                 }
76             }
77         }
78
79         contexts.add(context);
80     }
81
82     @Override
83     public void addLifecycleService(final LifecycleService lifecycleService) {
84         this.lifecycleService = lifecycleService;
85     }
86
87     @Override
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());
94         this.unMasterMe();
95         return Futures.transform(Futures.successfulAsList(futureList), new Function<List<Void>, Void>() {
96             @Nullable
97             @Override
98             public Void apply(@Nullable List<Void> input) {
99                 LOG.info("Closed clustering MASTER services for node {}", deviceContext.getDeviceInfo().getLOGValue());
100                 return null;
101             }
102         });
103     }
104
105     private void unMasterMe() {
106         this.registryFilling.set(false);
107         this.initialSubmitting.set(false);
108         this.initialGathering.set(false);
109         this.masterStateOnDevice.set(false);
110     }
111
112     @Override
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);
117         }
118         lifecycleService.close();
119         deviceContext.close();
120         rpcContext.close();
121         statisticsContext.close();
122     }
123
124     @Override
125     public void makeContextChainStateSlave() {
126         this.unMasterMe();
127         changeState(ContextChainState.WORKING_SLAVE);
128     }
129
130     @Override
131     public ListenableFuture<Void> connectionDropped() {
132         if (this.contextChainState == ContextChainState.WORKING_MASTER) {
133             return this.stopChain();
134         }
135         this.unMasterMe();
136         return Futures.immediateFuture(null);
137     }
138
139     @Override
140     public void registerServices(final ClusterSingletonServiceProvider clusterSingletonServiceProvider) {
141         this.lifecycleService.registerService(
142                 clusterSingletonServiceProvider,
143                 this.deviceContext);
144     }
145
146     @Override
147     public void makeDeviceSlave() {
148         this.unMasterMe();
149         this.lifecycleService.makeDeviceSlave(this.deviceContext);
150     }
151
152     @Override
153     public boolean isMastered(@Nonnull ContextChainMastershipState mastershipState) {
154         switch (mastershipState) {
155             case INITIAL_SUBMIT:
156                 LOG.debug("Device {}, initial submit OK.", deviceInfo.getLOGValue());
157                 this.initialSubmitting.set(true);
158                 break;
159             case MASTER_ON_DEVICE:
160                 LOG.debug("Device {}, master state OK.", deviceInfo.getLOGValue());
161                 this.masterStateOnDevice.set(true);
162                 break;
163             case INITIAL_GATHERING:
164                 LOG.debug("Device {}, initial gathering OK.", deviceInfo.getLOGValue());
165                 this.initialGathering.set(true);
166                 break;
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);
171             case CHECK:
172             default:
173         }
174         final boolean result =
175                 this.initialGathering.get() &&
176                 this.masterStateOnDevice.get() &&
177                 this.initialSubmitting.get();
178
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);
184         }
185         return result;
186     }
187
188     @Override
189     public boolean hasState() {
190         return contextChainState == ContextChainState.WORKING_MASTER
191                 || contextChainState == ContextChainState.WORKING_SLAVE;
192     }
193
194     @Override
195     public boolean addAuxiliaryConnection(@Nonnull ConnectionContext connectionContext) {
196         if (this.primaryConnection.getConnectionState() != ConnectionContext.CONNECTION_STATE.RIP) {
197             this.auxiliaryConnections.add(connectionContext);
198             return true;
199         } else {
200             return false;
201         }
202     }
203
204     @Override
205     public boolean auxiliaryConnectionDropped(@Nonnull ConnectionContext connectionContext) {
206         if (this.auxiliaryConnections.isEmpty()) {
207             return false;
208         }
209         if (!this.auxiliaryConnections.contains(connectionContext)) {
210             return false;
211         }
212         this.auxiliaryConnections.remove(connectionContext);
213         return true;
214     }
215
216     private void changeState(final ContextChainState contextChainState) {
217         boolean propagate = this.contextChainState == ContextChainState.UNDEFINED;
218         this.contextChainState = contextChainState;
219
220         if (propagate) {
221             contexts.stream()
222                     .filter(ContextChainStateListener.class::isInstance)
223                     .map(ContextChainStateListener.class::cast)
224                     .forEach(listener -> listener.onStateAcquired(contextChainState));
225         }
226     }
227 }