30eccf1db3b0049e81f8774239b20144ee598eab
[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         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     /**
200      * This method returns safe nodeId for logging
201      * @return string value od nodeId or string "null"
202      */
203     private String getSafeNodeIdForLOG() {
204         return null == nodeId ? "null" : nodeId.getValue();
205     }
206
207     @Override
208     public void setOutboundQueueHandleRegistration(OutboundQueueHandlerRegistration<OutboundQueueProvider> outboundQueueHandlerRegistration) {
209         this.outboundQueueHandlerRegistration = outboundQueueHandlerRegistration;
210     }
211
212     private void unregisterOutboundQueue() {
213         LOG.debug("Trying unregister outbound queue handler registration for node {}", nodeId);
214         if (outboundQueueHandlerRegistration != null) {
215             outboundQueueHandlerRegistration.close();
216             outboundQueueHandlerRegistration = null;
217         }
218     }
219
220     @Override
221     public synchronized void changeStateToHandshaking() {
222         connectionState = CONNECTION_STATE.HANDSHAKING;
223     }
224
225     @Override
226     public synchronized void changeStateToTimeouting() {
227         connectionState = CONNECTION_STATE.TIMEOUTING;
228     }
229
230     @Override
231     public synchronized void changeStateToWorking() {
232         connectionState = CONNECTION_STATE.WORKING;
233     }
234
235     @Override
236     public DeviceInfo getDeviceInfo() {
237         return this.deviceInfo;
238     }
239
240     @Override
241     public void handshakeSuccessful() {
242         Preconditions.checkNotNull(nodeId, "Cannot create DeviceInfo if 'NodeId' is not set!");
243         Preconditions.checkNotNull(featuresReply, "Cannot create DeviceInfo if 'features' is not set!");
244         this.deviceInfo = new DeviceInfoImpl(
245                 nodeId,
246                 DeviceStateUtil.createNodeInstanceIdentifier(nodeId),
247                 featuresReply.getVersion(),
248                 featuresReply.getDatapathId(),
249                 outboundQueueProvider);
250     }
251
252     @Override
253     public void setHandshakeContext(HandshakeContext handshakeContext) {
254         this.handshakeContext = handshakeContext;
255     }
256
257     @Override
258     public boolean equals(Object o) {
259         if (this == o) {
260             return true;
261         }
262
263         if (o == null || getClass() != o.getClass()) {
264             return false;
265         }
266
267         ConnectionContextImpl that = (ConnectionContextImpl) o;
268
269         if (!connectionAdapter.equals(that.connectionAdapter)) {
270             return false;
271         }
272
273         if (featuresReply != null ? !featuresReply.equals(that.featuresReply) : that.featuresReply != null) {
274             return false;
275         }
276
277         return nodeId != null ? nodeId.equals(that.nodeId) : that.nodeId == null;
278
279     }
280
281     @Override
282     public int hashCode() {
283         int result = connectionAdapter.hashCode();
284         result = 31 * result + (featuresReply != null ? featuresReply.hashCode() : 0);
285         result = 31 * result + (nodeId != null ? nodeId.hashCode() : 0);
286         return result;
287     }
288
289     private class DeviceInfoImpl implements DeviceInfo {
290
291         private final NodeId nodeId;
292         private final KeyedInstanceIdentifier<Node, NodeKey> nodeII;
293         private final Short version;
294         private final BigInteger datapathId;
295         private final ServiceGroupIdentifier serviceGroupIdentifier;
296         private OutboundQueue outboundQueueProvider;
297
298         DeviceInfoImpl(
299                 final NodeId nodeId,
300                 final KeyedInstanceIdentifier<Node, NodeKey> nodeII,
301                 final Short version,
302                 final BigInteger datapathId,
303                 final OutboundQueue outboundQueueProvider) {
304             this.nodeId = nodeId;
305             this.nodeII = nodeII;
306             this.version = version;
307             this.datapathId = datapathId;
308             this.outboundQueueProvider = outboundQueueProvider;
309             this.serviceGroupIdentifier = ServiceGroupIdentifier.create(this.nodeId.getValue());
310         }
311
312         @Override
313         public NodeId getNodeId() {
314             return nodeId;
315         }
316
317         @Override
318         public KeyedInstanceIdentifier<Node, NodeKey> getNodeInstanceIdentifier() {
319             return nodeII;
320         }
321
322         @Override
323         public Short getVersion() {
324             return version;
325         }
326
327         @Override
328         public BigInteger getDatapathId() {
329             return datapathId;
330         }
331
332         @Override
333         public ServiceGroupIdentifier getServiceIdentifier() {
334             return this.serviceGroupIdentifier;
335         }
336
337         @Override
338         public boolean equals(Object o) {
339             if (this == o) {
340                 return true;
341             }
342
343             if (o == null || getClass() != o.getClass()) {
344                 return false;
345             }
346
347             DeviceInfoImpl that = (DeviceInfoImpl) o;
348
349             return  (nodeId.equals(that.nodeId) &&
350                     nodeII.equals(that.nodeII) &&
351                     version.equals(that.version) &&
352                     datapathId.equals(that.datapathId));
353
354         }
355
356         @Override
357         public int hashCode() {
358             int result = nodeId.hashCode();
359             result = 31 * result + nodeII.hashCode();
360             result = 31 * result + version.hashCode();
361             result = 31 * result + datapathId.hashCode();
362             return result;
363         }
364
365         public void setOutboundQueueProvider(final OutboundQueue outboundQueueProvider) {
366             this.outboundQueueProvider = outboundQueueProvider;
367         }
368
369         @Override
370         public Long reserveXidForDeviceMessage() {
371             return outboundQueueProvider.reserveEntry();
372         }
373     }
374 }