986bbb178e4fae8ecac206b4c1c4c5095601a1c9
[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 {} ", getSafeNodeIdForLOG());
133             LOG.trace("Unregister outbound queue throws exception for node {} ", getSafeNodeIdForLOG(), 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 {}", getSafeNodeIdForLOG());
144             propagateDeviceDisconnectedEvent();
145         } else {
146             LOG.debug("Close connection without propagating for node {}", getSafeNodeIdForLOG());
147         }
148     }
149
150     private void closeHandshakeContext() {
151         LOG.debug("Trying closing handshake context for node {}", getSafeNodeIdForLOG());
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
166         connectionState = ConnectionContext.CONNECTION_STATE.RIP;
167
168         if (null == nodeId){
169             SessionStatistics.countEvent(connectionAdapter.getRemoteAddress().toString(), SessionStatistics.ConnectionStatus.CONNECTION_DISCONNECTED_BY_DEVICE);
170         } else {
171             SessionStatistics.countEvent(nodeId.toString(), SessionStatistics.ConnectionStatus.CONNECTION_DISCONNECTED_BY_DEVICE);
172         }
173
174         final InetSocketAddress remoteAddress = connectionAdapter.getRemoteAddress();
175         final Short auxiliaryId;
176         if (null != getFeatures() && null != getFeatures().getAuxiliaryId()) {
177             auxiliaryId = getFeatures().getAuxiliaryId();
178         } else {
179             auxiliaryId = 0;
180         }
181
182         LOG.debug("disconnecting: node={}|auxId={}|connection state = {}",
183                 remoteAddress,
184                 auxiliaryId,
185                 getConnectionState());
186
187         unregisterOutboundQueue();
188         closeHandshakeContext();
189         propagateDeviceDisconnectedEvent();
190     }
191
192     private void propagateDeviceDisconnectedEvent() {
193         if (null != deviceDisconnectedHandler) {
194             final BigInteger datapathId = featuresReply != null ? featuresReply.getDatapathId() : BigInteger.ZERO;
195             LOG.debug("Propagating connection closed event: {}, datapathId:{}.",
196                     connectionAdapter.getRemoteAddress(), datapathId);
197             deviceDisconnectedHandler.onDeviceDisconnected(this);
198         }
199     }
200
201     /**
202      * This method returns safe nodeId for logging
203      * @return string value od nodeId or string "null"
204      */
205     @Override
206     public String getSafeNodeIdForLOG() {
207         return null == nodeId ? "null" : nodeId.getValue();
208     }
209
210     @Override
211     public void setOutboundQueueHandleRegistration(OutboundQueueHandlerRegistration<OutboundQueueProvider> outboundQueueHandlerRegistration) {
212         this.outboundQueueHandlerRegistration = outboundQueueHandlerRegistration;
213     }
214
215     private void unregisterOutboundQueue() {
216         LOG.debug("Trying unregister outbound queue handler registration for node {}", nodeId);
217         if (outboundQueueHandlerRegistration != null) {
218             outboundQueueHandlerRegistration.close();
219             outboundQueueHandlerRegistration = null;
220         }
221     }
222
223     @Override
224     public synchronized void changeStateToHandshaking() {
225         connectionState = CONNECTION_STATE.HANDSHAKING;
226     }
227
228     @Override
229     public synchronized void changeStateToTimeouting() {
230         connectionState = CONNECTION_STATE.TIMEOUTING;
231     }
232
233     @Override
234     public synchronized void changeStateToWorking() {
235         connectionState = CONNECTION_STATE.WORKING;
236     }
237
238     @Override
239     public DeviceInfo getDeviceInfo() {
240         return this.deviceInfo;
241     }
242
243     @Override
244     public void handshakeSuccessful() {
245         Preconditions.checkNotNull(nodeId, "Cannot create DeviceInfo if 'NodeId' is not set!");
246         Preconditions.checkNotNull(featuresReply, "Cannot create DeviceInfo if 'features' is not set!");
247         this.deviceInfo = new DeviceInfoImpl(
248                 nodeId,
249                 DeviceStateUtil.createNodeInstanceIdentifier(nodeId),
250                 featuresReply.getVersion(),
251                 featuresReply.getDatapathId(),
252                 outboundQueueProvider);
253     }
254
255     @Override
256     public void setHandshakeContext(HandshakeContext handshakeContext) {
257         this.handshakeContext = handshakeContext;
258     }
259
260     @Override
261     public boolean equals(Object o) {
262         if (this == o) {
263             return true;
264         }
265
266         if (o == null || getClass() != o.getClass()) {
267             return false;
268         }
269
270         ConnectionContextImpl that = (ConnectionContextImpl) o;
271
272         if (!connectionAdapter.equals(that.connectionAdapter)) {
273             return false;
274         }
275
276         if (featuresReply != null ? !featuresReply.equals(that.featuresReply) : that.featuresReply != null) {
277             return false;
278         }
279
280         return nodeId != null ? nodeId.equals(that.nodeId) : that.nodeId == null;
281
282     }
283
284     @Override
285     public int hashCode() {
286         int result = connectionAdapter.hashCode();
287         result = 31 * result + (featuresReply != null ? featuresReply.hashCode() : 0);
288         result = 31 * result + (nodeId != null ? nodeId.hashCode() : 0);
289         return result;
290     }
291
292     private class DeviceInfoImpl implements DeviceInfo {
293
294         private final NodeId nodeId;
295         private final KeyedInstanceIdentifier<Node, NodeKey> nodeII;
296         private final Short version;
297         private final BigInteger datapathId;
298         private final ServiceGroupIdentifier serviceGroupIdentifier;
299         private OutboundQueue outboundQueueProvider;
300
301         DeviceInfoImpl(
302                 final NodeId nodeId,
303                 final KeyedInstanceIdentifier<Node, NodeKey> nodeII,
304                 final Short version,
305                 final BigInteger datapathId,
306                 final OutboundQueue outboundQueueProvider) {
307             this.nodeId = nodeId;
308             this.nodeII = nodeII;
309             this.version = version;
310             this.datapathId = datapathId;
311             this.outboundQueueProvider = outboundQueueProvider;
312             this.serviceGroupIdentifier = ServiceGroupIdentifier.create(this.nodeId.getValue());
313         }
314
315         @Override
316         public NodeId getNodeId() {
317             return nodeId;
318         }
319
320         @Override
321         public KeyedInstanceIdentifier<Node, NodeKey> getNodeInstanceIdentifier() {
322             return nodeII;
323         }
324
325         @Override
326         public Short getVersion() {
327             return version;
328         }
329
330         @Override
331         public BigInteger getDatapathId() {
332             return datapathId;
333         }
334
335         @Override
336         public ServiceGroupIdentifier getServiceIdentifier() {
337             return this.serviceGroupIdentifier;
338         }
339
340         @Override
341         public boolean equals(Object o) {
342             if (this == o) {
343                 return true;
344             }
345
346             if (o == null || getClass() != o.getClass()) {
347                 return false;
348             }
349
350             DeviceInfoImpl that = (DeviceInfoImpl) o;
351
352             return  (nodeId.equals(that.nodeId) &&
353                     nodeII.equals(that.nodeII) &&
354                     version.equals(that.version) &&
355                     datapathId.equals(that.datapathId));
356
357         }
358
359         @Override
360         public int hashCode() {
361             int result = nodeId.hashCode();
362             result = 31 * result + nodeII.hashCode();
363             result = 31 * result + version.hashCode();
364             result = 31 * result + datapathId.hashCode();
365             return result;
366         }
367
368         public void setOutboundQueueProvider(final OutboundQueue outboundQueueProvider) {
369             this.outboundQueueProvider = outboundQueueProvider;
370         }
371
372         @Override
373         public Long reserveXidForDeviceMessage() {
374             return outboundQueueProvider.reserveEntry();
375         }
376     }
377 }