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