Merge "Report (TCP) port number for switches"
[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.MoreObjects;
12 import com.google.common.base.Preconditions;
13 import java.math.BigInteger;
14 import java.util.ArrayList;
15 import java.util.Collections;
16 import java.util.List;
17 import java.util.Objects;
18 import java.util.Optional;
19 import org.opendaylight.mdsal.singleton.common.api.ServiceGroupIdentifier;
20 import org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter;
21 import org.opendaylight.openflowjava.protocol.api.connection.OutboundQueue;
22 import org.opendaylight.openflowjava.protocol.api.connection.OutboundQueueHandlerRegistration;
23 import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
24 import org.opendaylight.openflowplugin.api.openflow.connection.HandshakeContext;
25 import org.opendaylight.openflowplugin.api.openflow.connection.OutboundQueueProvider;
26 import org.opendaylight.openflowplugin.api.openflow.device.DeviceInfo;
27 import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceDisconnectedHandler;
28 import org.opendaylight.openflowplugin.impl.statistics.ofpspecific.SessionStatistics;
29 import org.opendaylight.openflowplugin.impl.util.DeviceStateUtil;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FeaturesReply;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.PortStatusMessage;
35 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 /**
40  *
41  */
42 public class ConnectionContextImpl implements ConnectionContext {
43
44     private final ConnectionAdapter connectionAdapter;
45     private volatile CONNECTION_STATE connectionState;
46     private FeaturesReply featuresReply;
47     private NodeId nodeId;
48     private DeviceDisconnectedHandler deviceDisconnectedHandler;
49     private static final Logger LOG = LoggerFactory.getLogger(ConnectionContextImpl.class);
50     private OutboundQueueProvider outboundQueueProvider;
51     private OutboundQueueHandlerRegistration<OutboundQueueProvider> outboundQueueHandlerRegistration;
52     private HandshakeContext handshakeContext;
53     private DeviceInfo deviceInfo;
54     private List<PortStatusMessage> portStatusMessages = new ArrayList<>();
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         disconnectDevice(propagate, true);
112     }
113
114     private void closeHandshakeContext() {
115         LOG.debug("Trying closing handshake context for node {}", getSafeNodeIdForLOG());
116         if (handshakeContext != null) {
117             try {
118                 handshakeContext.close();
119             } catch (Exception e) {
120                 LOG.error("handshake context closing failed:{} ", e);
121             } finally {
122                 handshakeContext = null;
123             }
124         }
125     }
126
127     @Override
128     public void onConnectionClosed() {
129         disconnectDevice(true, false);
130     }
131
132     private void disconnectDevice(final boolean propagate,
133                                   final boolean forced) {
134         final String device = Objects.nonNull(nodeId) ? nodeId.getValue() : getConnectionAdapter().getRemoteAddress().toString();
135         final short auxiliaryId = Optional
136                 .ofNullable(getFeatures())
137                 .flatMap(features -> Optional
138                         .ofNullable(features.getAuxiliaryId()))
139                 .orElse((short) 0);
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         SessionStatistics.countEvent(device, forced
148                 ? SessionStatistics.ConnectionStatus.CONNECTION_DISCONNECTED_BY_OFP
149                 : SessionStatistics.ConnectionStatus.CONNECTION_DISCONNECTED_BY_DEVICE);
150
151         connectionState = ConnectionContext.CONNECTION_STATE.RIP;
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 (Objects.nonNull(deviceDisconnectedHandler)) {
174             final BigInteger datapathId = featuresReply != null ? featuresReply.getDatapathId() : BigInteger.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      * This method returns safe nodeId for logging
185      * @return string value od nodeId or string "null"
186      */
187     @Override
188     public String getSafeNodeIdForLOG() {
189         return Objects.nonNull(nodeId) ? nodeId.getValue() : "null";
190     }
191
192     @Override
193     public void setOutboundQueueHandleRegistration(OutboundQueueHandlerRegistration<OutboundQueueProvider> outboundQueueHandlerRegistration) {
194         this.outboundQueueHandlerRegistration = outboundQueueHandlerRegistration;
195     }
196
197     private void unregisterOutboundQueue() {
198         if (LOG.isDebugEnabled()) {
199             LOG.debug("Trying unregister outbound queue handler registration for node {}", nodeId);
200         }
201         if (outboundQueueHandlerRegistration != null) {
202             outboundQueueHandlerRegistration.close();
203             outboundQueueHandlerRegistration = null;
204         }
205     }
206
207     @Override
208     public synchronized void changeStateToHandshaking() {
209         connectionState = CONNECTION_STATE.HANDSHAKING;
210     }
211
212     @Override
213     public synchronized void changeStateToTimeouting() {
214         connectionState = CONNECTION_STATE.TIMEOUTING;
215     }
216
217     @Override
218     public synchronized void changeStateToWorking() {
219         connectionState = CONNECTION_STATE.WORKING;
220     }
221
222     @Override
223     public void handlePortStatusMessage(final PortStatusMessage portStatusMessage) {
224         LOG.info("Received early port status message for node {} with reason {} and state {}",
225                 nodeId.getValue(),
226                 portStatusMessage.getReason(),
227                 MoreObjects.firstNonNull(portStatusMessage.getState(), portStatusMessage.getStateV10()));
228
229         LOG.debug("Early port status message body is {}", portStatusMessage);
230         portStatusMessages.add(portStatusMessage);
231     }
232
233     @Override
234     public List<PortStatusMessage> retrieveAndClearPortStatusMessages() {
235         final List<PortStatusMessage> immutablePortStatusMessages = Collections.unmodifiableList(portStatusMessages);
236         portStatusMessages.clear();
237         return immutablePortStatusMessages;
238     }
239
240     @Override
241     public DeviceInfo getDeviceInfo() {
242         return this.deviceInfo;
243     }
244
245     @Override
246     public void handshakeSuccessful() {
247         Preconditions.checkNotNull(nodeId, "Cannot create DeviceInfo if 'NodeId' is not set!");
248         Preconditions.checkNotNull(featuresReply, "Cannot create DeviceInfo if 'features' is not set!");
249         this.deviceInfo = new DeviceInfoImpl(
250                 nodeId,
251                 DeviceStateUtil.createNodeInstanceIdentifier(nodeId),
252                 featuresReply.getVersion(),
253                 featuresReply.getDatapathId(),
254                 outboundQueueProvider);
255     }
256
257     @Override
258     public void setHandshakeContext(HandshakeContext handshakeContext) {
259         this.handshakeContext = handshakeContext;
260     }
261
262     @Override
263     public boolean equals(Object o) {
264         if (this == o) {
265             return true;
266         }
267
268         if (o == null || getClass() != o.getClass()) {
269             return false;
270         }
271
272         ConnectionContextImpl that = (ConnectionContextImpl) o;
273
274         if (!connectionAdapter.equals(that.connectionAdapter)) {
275             return false;
276         }
277
278         if (featuresReply != null ? !featuresReply.equals(that.featuresReply) : that.featuresReply != null) {
279             return false;
280         }
281
282         return nodeId != null ? nodeId.equals(that.nodeId) : that.nodeId == null;
283
284     }
285
286     @Override
287     public int hashCode() {
288         int result = connectionAdapter.hashCode();
289         result = 31 * result + (featuresReply != null ? featuresReply.hashCode() : 0);
290         result = 31 * result + (nodeId != null ? nodeId.hashCode() : 0);
291         return result;
292     }
293
294     private class DeviceInfoImpl implements DeviceInfo {
295
296         private final NodeId nodeId;
297         private final KeyedInstanceIdentifier<Node, NodeKey> nodeII;
298         private final Short version;
299         private final BigInteger datapathId;
300         private final ServiceGroupIdentifier serviceGroupIdentifier;
301         private OutboundQueue outboundQueueProvider;
302
303         DeviceInfoImpl(
304                 final NodeId nodeId,
305                 final KeyedInstanceIdentifier<Node, NodeKey> nodeII,
306                 final Short version,
307                 final BigInteger datapathId,
308                 final OutboundQueue outboundQueueProvider) {
309             this.nodeId = nodeId;
310             this.nodeII = nodeII;
311             this.version = version;
312             this.datapathId = datapathId;
313             this.outboundQueueProvider = outboundQueueProvider;
314             this.serviceGroupIdentifier = ServiceGroupIdentifier.create(this.nodeId.getValue());
315         }
316
317         @Override
318         public NodeId getNodeId() {
319             return nodeId;
320         }
321
322         @Override
323         public KeyedInstanceIdentifier<Node, NodeKey> getNodeInstanceIdentifier() {
324             return nodeII;
325         }
326
327         @Override
328         public short getVersion() {
329             return version;
330         }
331
332         @Override
333         public BigInteger getDatapathId() {
334             return datapathId;
335         }
336
337         @Override
338         public ServiceGroupIdentifier getServiceIdentifier() {
339             return this.serviceGroupIdentifier;
340         }
341
342         @Override
343         public boolean equals(Object o) {
344             if (this == o) {
345                 return true;
346             }
347
348             if (o == null || getClass() != o.getClass()) {
349                 return false;
350             }
351
352             DeviceInfoImpl that = (DeviceInfoImpl) o;
353
354             return  (nodeId.equals(that.nodeId) &&
355                     nodeII.equals(that.nodeII) &&
356                     version.equals(that.version) &&
357                     datapathId.equals(that.datapathId));
358
359         }
360
361         @Override
362         public int hashCode() {
363             int result = nodeId.hashCode();
364             result = 31 * result + nodeII.hashCode();
365             result = 31 * result + version.hashCode();
366             result = 31 * result + datapathId.hashCode();
367             return result;
368         }
369
370         @Override
371         public String toString() {
372             return getLOGValue();
373         }
374
375         public void setOutboundQueueProvider(final OutboundQueue outboundQueueProvider) {
376             this.outboundQueueProvider = outboundQueueProvider;
377         }
378
379         @Override
380         public Long reserveXidForDeviceMessage() {
381             return outboundQueueProvider.reserveEntry();
382         }
383     }
384 }