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