Bug 6554 Fix rejecting connections
[openflowplugin.git] / openflowplugin-impl / src / main / java / org / opendaylight / openflowplugin / impl / connection / ConnectionContextImpl.java
1 /**
2  * Copyright (c) 2015 Cisco Systems, Inc. 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
9 package org.opendaylight.openflowplugin.impl.connection;
10
11 import com.google.common.base.Preconditions;
12 import java.math.BigInteger;
13 import java.net.InetSocketAddress;
14 import java.util.Objects;
15 import java.util.concurrent.Callable;
16 import java.util.concurrent.ExecutionException;
17 import java.util.concurrent.Executors;
18 import java.util.concurrent.Future;
19 import java.util.concurrent.TimeUnit;
20 import java.util.concurrent.TimeoutException;
21 import org.opendaylight.mdsal.singleton.common.api.ServiceGroupIdentifier;
22 import org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter;
23 import org.opendaylight.openflowjava.protocol.api.connection.OutboundQueue;
24 import org.opendaylight.openflowjava.protocol.api.connection.OutboundQueueHandlerRegistration;
25 import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
26 import org.opendaylight.openflowplugin.api.openflow.connection.HandshakeContext;
27 import org.opendaylight.openflowplugin.api.openflow.connection.OutboundQueueProvider;
28 import org.opendaylight.openflowplugin.api.openflow.device.DeviceInfo;
29 import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceDisconnectedHandler;
30 import org.opendaylight.openflowplugin.impl.statistics.ofpspecific.SessionStatistics;
31 import org.opendaylight.openflowplugin.impl.util.DeviceStateUtil;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FeaturesReply;
36 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 /**
41  *
42  */
43 public class ConnectionContextImpl implements ConnectionContext {
44
45     private final ConnectionAdapter connectionAdapter;
46     private volatile CONNECTION_STATE connectionState;
47     private FeaturesReply featuresReply;
48     private NodeId nodeId;
49     private DeviceDisconnectedHandler deviceDisconnectedHandler;
50     private static final Logger LOG = LoggerFactory.getLogger(ConnectionContextImpl.class);
51     private OutboundQueueProvider outboundQueueProvider;
52     private OutboundQueueHandlerRegistration<OutboundQueueProvider> outboundQueueHandlerRegistration;
53     private HandshakeContext handshakeContext;
54     private DeviceInfo deviceInfo;
55
56     /**
57      * @param connectionAdapter
58      */
59     public ConnectionContextImpl(final ConnectionAdapter connectionAdapter) {
60         this.connectionAdapter = connectionAdapter;
61     }
62
63     @Override
64     public ConnectionAdapter getConnectionAdapter() {
65         return connectionAdapter;
66     }
67
68     @Override
69     public OutboundQueue getOutboundQueueProvider() {
70         return this.outboundQueueProvider;
71     }
72
73     @Override
74     public void setOutboundQueueProvider(final OutboundQueueProvider outboundQueueProvider) {
75         this.outboundQueueProvider = outboundQueueProvider;
76         ((DeviceInfoImpl)this.deviceInfo).setOutboundQueueProvider(this.outboundQueueProvider);
77     }
78
79     @Override
80     public CONNECTION_STATE getConnectionState() {
81         return connectionState;
82     }
83
84     @Override
85     public NodeId getNodeId() {
86         return nodeId;
87     }
88
89     @Override
90     public void setNodeId(final NodeId nodeId) {
91         this.nodeId = nodeId;
92     }
93
94     @Override
95     public FeaturesReply getFeatures() {
96         return featuresReply;
97     }
98
99     @Override
100     public void setDeviceDisconnectedHandler(final DeviceDisconnectedHandler deviceDisconnectedHandler) {
101         this.deviceDisconnectedHandler = deviceDisconnectedHandler;
102     }
103
104     @Override
105     public void setFeatures(final FeaturesReply featuresReply) {
106         this.featuresReply = featuresReply;
107     }
108
109     @Override
110     public void closeConnection(final boolean propagate) {
111         if (Objects.isNull(nodeId)){
112             SessionStatistics.countEvent(connectionAdapter.getRemoteAddress().toString(), SessionStatistics.ConnectionStatus.CONNECTION_DISCONNECTED_BY_OFP);
113         } else {
114             SessionStatistics.countEvent(nodeId.toString(), SessionStatistics.ConnectionStatus.CONNECTION_DISCONNECTED_BY_OFP);
115         }
116         final BigInteger datapathId = Objects.nonNull(featuresReply) ? featuresReply.getDatapathId() : BigInteger.ZERO;
117         if (LOG.isDebugEnabled()) {
118             LOG.debug("Actively closing connection: {}, datapathId: {}",
119                     connectionAdapter.getRemoteAddress(), datapathId);
120         }
121         connectionState = ConnectionContext.CONNECTION_STATE.RIP;
122
123         unregisterOutboundQueue();
124         closeHandshakeContext();
125
126         if (getConnectionAdapter().isAlive()) {
127             getConnectionAdapter().disconnect();
128         }
129
130         if (propagate) {
131             propagateDeviceDisconnectedEvent();
132         }
133     }
134
135     private void closeHandshakeContext() {
136         LOG.debug("Trying closing handshake context for node {}", getSafeNodeIdForLOG());
137         if (handshakeContext != null) {
138             try {
139                 handshakeContext.close();
140             } catch (Exception e) {
141                 LOG.error("handshake context closing failed:{} ", e);
142             } finally {
143                 handshakeContext = null;
144             }
145         }
146     }
147
148     @Override
149     public void onConnectionClosed() {
150
151         connectionState = ConnectionContext.CONNECTION_STATE.RIP;
152
153         if (null == nodeId){
154             SessionStatistics.countEvent(connectionAdapter.getRemoteAddress().toString(), SessionStatistics.ConnectionStatus.CONNECTION_DISCONNECTED_BY_DEVICE);
155         } else {
156             SessionStatistics.countEvent(nodeId.toString(), SessionStatistics.ConnectionStatus.CONNECTION_DISCONNECTED_BY_DEVICE);
157         }
158
159         final InetSocketAddress remoteAddress = connectionAdapter.getRemoteAddress();
160         final Short auxiliaryId;
161         if (null != getFeatures() && null != getFeatures().getAuxiliaryId()) {
162             auxiliaryId = getFeatures().getAuxiliaryId();
163         } else {
164             auxiliaryId = 0;
165         }
166
167         LOG.debug("disconnecting: node={}|auxId={}|connection state = {}",
168                 remoteAddress,
169                 auxiliaryId,
170                 getConnectionState());
171
172         unregisterOutboundQueue();
173         closeHandshakeContext();
174         propagateDeviceDisconnectedEvent();
175     }
176
177     private void propagateDeviceDisconnectedEvent() {
178         if (Objects.nonNull(deviceDisconnectedHandler)) {
179             final BigInteger datapathId = featuresReply != null ? featuresReply.getDatapathId() : BigInteger.ZERO;
180             if (LOG.isDebugEnabled()) {
181                 LOG.debug("Propagating connection closed event: {}, datapathId:{}.",
182                         connectionAdapter.getRemoteAddress(), datapathId);
183             }
184             deviceDisconnectedHandler.onDeviceDisconnected(this);
185         }
186     }
187
188     /**
189      * This method returns safe nodeId for logging
190      * @return string value od nodeId or string "null"
191      */
192     @Override
193     public String getSafeNodeIdForLOG() {
194         return Objects.nonNull(nodeId) ? nodeId.getValue() : "null";
195     }
196
197     @Override
198     public void setOutboundQueueHandleRegistration(OutboundQueueHandlerRegistration<OutboundQueueProvider> outboundQueueHandlerRegistration) {
199         this.outboundQueueHandlerRegistration = outboundQueueHandlerRegistration;
200     }
201
202     private void unregisterOutboundQueue() {
203         if (LOG.isDebugEnabled()) {
204             LOG.debug("Trying unregister outbound queue handler registration for node {}", nodeId);
205         }
206         if (outboundQueueHandlerRegistration != null) {
207             outboundQueueHandlerRegistration.close();
208             outboundQueueHandlerRegistration = null;
209         }
210     }
211
212     @Override
213     public synchronized void changeStateToHandshaking() {
214         connectionState = CONNECTION_STATE.HANDSHAKING;
215     }
216
217     @Override
218     public synchronized void changeStateToTimeouting() {
219         connectionState = CONNECTION_STATE.TIMEOUTING;
220     }
221
222     @Override
223     public synchronized void changeStateToWorking() {
224         connectionState = CONNECTION_STATE.WORKING;
225     }
226
227     @Override
228     public DeviceInfo getDeviceInfo() {
229         return this.deviceInfo;
230     }
231
232     @Override
233     public void handshakeSuccessful() {
234         Preconditions.checkNotNull(nodeId, "Cannot create DeviceInfo if 'NodeId' is not set!");
235         Preconditions.checkNotNull(featuresReply, "Cannot create DeviceInfo if 'features' is not set!");
236         this.deviceInfo = new DeviceInfoImpl(
237                 nodeId,
238                 DeviceStateUtil.createNodeInstanceIdentifier(nodeId),
239                 featuresReply.getVersion(),
240                 featuresReply.getDatapathId(),
241                 outboundQueueProvider);
242     }
243
244     @Override
245     public void setHandshakeContext(HandshakeContext handshakeContext) {
246         this.handshakeContext = handshakeContext;
247     }
248
249     @Override
250     public boolean equals(Object o) {
251         if (this == o) {
252             return true;
253         }
254
255         if (o == null || getClass() != o.getClass()) {
256             return false;
257         }
258
259         ConnectionContextImpl that = (ConnectionContextImpl) o;
260
261         if (!connectionAdapter.equals(that.connectionAdapter)) {
262             return false;
263         }
264
265         if (featuresReply != null ? !featuresReply.equals(that.featuresReply) : that.featuresReply != null) {
266             return false;
267         }
268
269         return nodeId != null ? nodeId.equals(that.nodeId) : that.nodeId == null;
270
271     }
272
273     @Override
274     public int hashCode() {
275         int result = connectionAdapter.hashCode();
276         result = 31 * result + (featuresReply != null ? featuresReply.hashCode() : 0);
277         result = 31 * result + (nodeId != null ? nodeId.hashCode() : 0);
278         return result;
279     }
280
281     private class DeviceInfoImpl implements DeviceInfo {
282
283         private final NodeId nodeId;
284         private final KeyedInstanceIdentifier<Node, NodeKey> nodeII;
285         private final Short version;
286         private final BigInteger datapathId;
287         private final ServiceGroupIdentifier serviceGroupIdentifier;
288         private OutboundQueue outboundQueueProvider;
289
290         DeviceInfoImpl(
291                 final NodeId nodeId,
292                 final KeyedInstanceIdentifier<Node, NodeKey> nodeII,
293                 final Short version,
294                 final BigInteger datapathId,
295                 final OutboundQueue outboundQueueProvider) {
296             this.nodeId = nodeId;
297             this.nodeII = nodeII;
298             this.version = version;
299             this.datapathId = datapathId;
300             this.outboundQueueProvider = outboundQueueProvider;
301             this.serviceGroupIdentifier = ServiceGroupIdentifier.create(this.nodeId.getValue());
302         }
303
304         @Override
305         public NodeId getNodeId() {
306             return nodeId;
307         }
308
309         @Override
310         public KeyedInstanceIdentifier<Node, NodeKey> getNodeInstanceIdentifier() {
311             return nodeII;
312         }
313
314         @Override
315         public short getVersion() {
316             return version;
317         }
318
319         @Override
320         public BigInteger getDatapathId() {
321             return datapathId;
322         }
323
324         @Override
325         public ServiceGroupIdentifier getServiceIdentifier() {
326             return this.serviceGroupIdentifier;
327         }
328
329         @Override
330         public boolean equals(Object o) {
331             if (this == o) {
332                 return true;
333             }
334
335             if (o == null || getClass() != o.getClass()) {
336                 return false;
337             }
338
339             DeviceInfoImpl that = (DeviceInfoImpl) o;
340
341             return  (nodeId.equals(that.nodeId) &&
342                     nodeII.equals(that.nodeII) &&
343                     version.equals(that.version) &&
344                     datapathId.equals(that.datapathId));
345
346         }
347
348         @Override
349         public int hashCode() {
350             int result = nodeId.hashCode();
351             result = 31 * result + nodeII.hashCode();
352             result = 31 * result + version.hashCode();
353             result = 31 * result + datapathId.hashCode();
354             return result;
355         }
356
357         public void setOutboundQueueProvider(final OutboundQueue outboundQueueProvider) {
358             this.outboundQueueProvider = outboundQueueProvider;
359         }
360
361         @Override
362         public Long reserveXidForDeviceMessage() {
363             return outboundQueueProvider.reserveEntry();
364         }
365     }
366 }