Bug 5596 Changes when closing device
[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 (null == 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 = featuresReply != null ? featuresReply.getDatapathId() : BigInteger.ZERO;
117         LOG.debug("Actively closing connection: {}, datapathId: {}",
118                 connectionAdapter.getRemoteAddress(), datapathId);
119         connectionState = ConnectionContext.CONNECTION_STATE.RIP;
120
121         Future<Void> future = Executors.newSingleThreadExecutor().submit(new Callable<Void>() {
122             @Override
123             public Void call() throws Exception {
124                 unregisterOutboundQueue();
125                 return null;
126             }
127         });
128         try {
129             future.get(1, TimeUnit.SECONDS);
130             LOG.info("Unregister outbound queue successful.");
131         } catch (InterruptedException | TimeoutException | ExecutionException e) {
132             LOG.warn("Unregister outbound queue throws exception for node {} ", nodeId);
133             LOG.trace("Unregister outbound queue throws exception for node {} ", nodeId, e);
134         }
135
136         closeHandshakeContext();
137
138         if (getConnectionAdapter().isAlive()) {
139             getConnectionAdapter().disconnect();
140         }
141
142         if (propagate) {
143             LOG.debug("Propagating device disconnect for node {}", nodeId);
144             propagateDeviceDisconnectedEvent();
145         } else {
146             LOG.debug("Close connection without propagating for node {}", nodeId);
147         }
148     }
149
150     private void closeHandshakeContext() {
151         LOG.debug("Trying closing handshake context for node {}", nodeId);
152         if (handshakeContext != null) {
153             try {
154                 handshakeContext.close();
155             } catch (Exception e) {
156                 LOG.error("handshake context closing failed:{} ", e);
157             } finally {
158                 handshakeContext = null;
159             }
160         }
161     }
162
163     @Override
164     public void onConnectionClosed() {
165         if (null == nodeId){
166             SessionStatistics.countEvent(connectionAdapter.getRemoteAddress().toString(), SessionStatistics.ConnectionStatus.CONNECTION_DISCONNECTED_BY_DEVICE);
167         } else {
168             SessionStatistics.countEvent(nodeId.toString(), SessionStatistics.ConnectionStatus.CONNECTION_DISCONNECTED_BY_DEVICE);
169         }
170         connectionState = ConnectionContext.CONNECTION_STATE.RIP;
171
172         final InetSocketAddress remoteAddress = connectionAdapter.getRemoteAddress();
173         final Short auxiliaryId;
174         if (null != getFeatures() && null != getFeatures().getAuxiliaryId()) {
175             auxiliaryId = getFeatures().getAuxiliaryId();
176         } else {
177             auxiliaryId = 0;
178         }
179
180         LOG.debug("disconnecting: node={}|auxId={}|connection state = {}",
181                 remoteAddress,
182                 auxiliaryId,
183                 getConnectionState());
184
185         unregisterOutboundQueue();
186         closeHandshakeContext();
187         propagateDeviceDisconnectedEvent();
188     }
189
190     private void propagateDeviceDisconnectedEvent() {
191         if (null != deviceDisconnectedHandler) {
192             final BigInteger datapathId = featuresReply != null ? featuresReply.getDatapathId() : BigInteger.ZERO;
193             LOG.debug("Propagating connection closed event: {}, datapathId:{}.",
194                     connectionAdapter.getRemoteAddress(), datapathId);
195             deviceDisconnectedHandler.onDeviceDisconnected(this);
196         }
197     }
198
199     @Override
200     public void setOutboundQueueHandleRegistration(OutboundQueueHandlerRegistration<OutboundQueueProvider> outboundQueueHandlerRegistration) {
201         this.outboundQueueHandlerRegistration = outboundQueueHandlerRegistration;
202     }
203
204     private void unregisterOutboundQueue() {
205         LOG.debug("Trying unregister outbound queue handler registration for node {}", nodeId);
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 }