Merge "Remove device without master from DS."
[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.LifecycleService;
29 import org.opendaylight.openflowplugin.api.openflow.rpc.RpcContext;
30 import org.opendaylight.openflowplugin.api.openflow.statistics.StatisticsContext;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 public class ContextChainImpl implements ContextChain {
35
36     private static final Logger LOG = LoggerFactory.getLogger(ContextChainImpl.class);
37
38     private Set<OFPContext> contexts = new ConcurrentSet<>();
39     private StatisticsContext statisticsContext;
40     private DeviceContext deviceContext;
41     private RpcContext rpcContext;
42     private LifecycleService lifecycleService;
43     private DeviceInfo deviceInfo;
44     private ConnectionContext primaryConnection;
45     private Set<ConnectionContext> auxiliaryConnections = new ConcurrentSet<>();
46
47     private volatile ContextChainState contextChainState;
48
49     private AtomicBoolean masterStateOnDevice;
50     private AtomicBoolean initialGathering;
51     private AtomicBoolean initialSubmitting;
52     private AtomicBoolean registryFilling;
53
54     ContextChainImpl(final ConnectionContext connectionContext) {
55         this.primaryConnection = connectionContext;
56         this.contextChainState = ContextChainState.UNDEFINED;
57         this.masterStateOnDevice = new AtomicBoolean(false);
58         this.initialGathering = new AtomicBoolean(false);
59         this.initialSubmitting = new AtomicBoolean(false);
60         this.registryFilling = new AtomicBoolean(false);
61         this.deviceInfo = connectionContext.getDeviceInfo();
62     }
63
64     @Override
65     public <T extends OFPContext> void addContext(final T context) {
66         if (context instanceof StatisticsContext) {
67             this.statisticsContext = (StatisticsContext) context;
68         } else {
69             if (context instanceof DeviceContext) {
70                 this.deviceContext = (DeviceContext) context;
71             } else {
72                 if (context instanceof RpcContext) {
73                     this.rpcContext = (RpcContext) context;
74                 }
75             }
76         }
77         contexts.add(context);
78     }
79
80     @Override
81     public void addLifecycleService(final LifecycleService lifecycleService) {
82         this.lifecycleService = lifecycleService;
83     }
84
85     @Override
86     public ListenableFuture<Void> stopChain() {
87         //TODO: stopClusterServices change parameter
88         final List<ListenableFuture<Void>> futureList = new ArrayList<>();
89         futureList.add(statisticsContext.stopClusterServices());
90         futureList.add(rpcContext.stopClusterServices());
91         futureList.add(deviceContext.stopClusterServices());
92         this.unMasterMe();
93         return Futures.transform(Futures.successfulAsList(futureList), new Function<List<Void>, Void>() {
94             @Nullable
95             @Override
96             public Void apply(@Nullable List<Void> input) {
97                 LOG.info("Closed clustering MASTER services for node {}", deviceContext.getDeviceInfo().getLOGValue());
98                 return null;
99             }
100         });
101     }
102
103     private void unMasterMe() {
104         this.registryFilling.set(false);
105         this.initialSubmitting.set(false);
106         this.initialGathering.set(false);
107         this.masterStateOnDevice.set(false);
108     }
109
110     @Override
111     public void close() {
112         this.auxiliaryConnections.forEach(connectionContext -> connectionContext.closeConnection(false));
113         if (this.primaryConnection.getConnectionState() != ConnectionContext.CONNECTION_STATE.RIP) {
114             this.primaryConnection.closeConnection(true);
115         }
116         lifecycleService.close();
117         deviceContext.close();
118         rpcContext.close();
119         statisticsContext.close();
120     }
121
122     @Override
123     public void makeContextChainStateSlave() {
124         this.unMasterMe();
125         this.contextChainState = ContextChainState.WORKING_SLAVE;
126     }
127
128     @Override
129     public ListenableFuture<Void> connectionDropped() {
130         if (this.contextChainState == ContextChainState.WORKING_MASTER) {
131             return this.stopChain();
132         }
133         this.unMasterMe();
134         return Futures.immediateFuture(null);
135     }
136
137     @Override
138     public void registerServices(final ClusterSingletonServiceProvider clusterSingletonServiceProvider) {
139         this.lifecycleService.registerService(
140                 clusterSingletonServiceProvider,
141                 this.deviceContext);
142     }
143
144     @Override
145     public void makeDeviceSlave() {
146         this.unMasterMe();
147         this.lifecycleService.makeDeviceSlave(this.deviceContext);
148     }
149
150     @Override
151     public boolean isMastered(@Nonnull ContextChainMastershipState mastershipState) {
152         switch (mastershipState) {
153             case INITIAL_SUBMIT:
154                 LOG.debug("Device {}, initial submit OK.", deviceInfo.getLOGValue());
155                 this.initialSubmitting.set(true);
156                 break;
157             case MASTER_ON_DEVICE:
158                 LOG.debug("Device {}, master state OK.", deviceInfo.getLOGValue());
159                 this.masterStateOnDevice.set(true);
160                 break;
161             case INITIAL_GATHERING:
162                 LOG.debug("Device {}, initial gathering OK.", deviceInfo.getLOGValue());
163                 this.initialGathering.set(true);
164                 break;
165             //Flow registry fill is not mandatory to work as a master
166             case INITIAL_FLOW_REGISTRY_FILL:
167                 LOG.debug("Device {}, initial registry filling OK.", deviceInfo.getLOGValue());
168                 this.registryFilling.set(true);
169             case CHECK:
170             default:
171         }
172         final boolean result =
173                 this.initialGathering.get() &&
174                 this.masterStateOnDevice.get() &&
175                 this.initialSubmitting.get();
176
177         if (result && mastershipState != ContextChainMastershipState.CHECK) {
178             LOG.info("Device {} is able to work as master{}",
179                     deviceInfo.getLOGValue(),
180                     this.registryFilling.get() ? " WITHOUT flow registry !!!" : ".");
181             contextChainState = ContextChainState.WORKING_MASTER;
182         }
183         return result;
184     }
185
186     @Override
187     public boolean hasState() {
188         return contextChainState == ContextChainState.WORKING_MASTER
189                 || contextChainState == ContextChainState.WORKING_SLAVE;
190     }
191
192     @Override
193     public boolean addAuxiliaryConnection(@Nonnull ConnectionContext connectionContext) {
194         if (this.primaryConnection.getConnectionState() != ConnectionContext.CONNECTION_STATE.RIP) {
195             this.auxiliaryConnections.add(connectionContext);
196             return true;
197         } else {
198             return false;
199         }
200     }
201
202     @Override
203     public boolean auxiliaryConnectionDropped(@Nonnull ConnectionContext connectionContext) {
204         if (this.auxiliaryConnections.isEmpty()) {
205             return false;
206         }
207         if (!this.auxiliaryConnections.contains(connectionContext)) {
208             return false;
209         }
210         this.auxiliaryConnections.remove(connectionContext);
211         return true;
212     }
213 }