OPNFLWPLUG-1049 : Device connection hold time to maintain delay between
[openflowplugin.git] / openflowplugin-impl / src / main / java / org / opendaylight / openflowplugin / impl / connection / ConnectionManagerImpl.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.annotations.VisibleForTesting;
11 import com.google.common.base.Preconditions;
12 import java.math.BigInteger;
13 import java.net.InetAddress;
14 import java.time.LocalDateTime;
15 import java.util.Collection;
16 import java.util.Map;
17 import java.util.concurrent.ConcurrentHashMap;
18 import java.util.concurrent.ExecutorService;
19 import org.eclipse.jdt.annotation.NonNull;
20 import org.opendaylight.mdsal.binding.api.ClusteredDataTreeChangeListener;
21 import org.opendaylight.mdsal.binding.api.DataBroker;
22 import org.opendaylight.mdsal.binding.api.DataObjectModification;
23 import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
24 import org.opendaylight.mdsal.binding.api.DataTreeModification;
25 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
26 import org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter;
27 import org.opendaylight.openflowjava.protocol.api.connection.ConnectionReadyListener;
28 import org.opendaylight.openflowplugin.api.OFConstants;
29 import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
30 import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionManager;
31 import org.opendaylight.openflowplugin.api.openflow.connection.DeviceConnectionStatusProvider;
32 import org.opendaylight.openflowplugin.api.openflow.connection.HandshakeContext;
33 import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceConnectedHandler;
34 import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceDisconnectedHandler;
35 import org.opendaylight.openflowplugin.api.openflow.md.core.HandshakeListener;
36 import org.opendaylight.openflowplugin.api.openflow.md.core.HandshakeManager;
37 import org.opendaylight.openflowplugin.impl.common.DeviceConnectionRateLimiter;
38 import org.opendaylight.openflowplugin.impl.connection.listener.ConnectionReadyListenerImpl;
39 import org.opendaylight.openflowplugin.impl.connection.listener.HandshakeListenerImpl;
40 import org.opendaylight.openflowplugin.impl.connection.listener.OpenflowProtocolListenerInitialImpl;
41 import org.opendaylight.openflowplugin.impl.connection.listener.SystemNotificationsListenerImpl;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OpenflowProtocolListener;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.system.rev130927.SystemNotificationsListener;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow.provider.config.rev160510.OpenflowProviderConfig;
47 import org.opendaylight.yangtools.concepts.ListenerRegistration;
48 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51
52 public class ConnectionManagerImpl implements ConnectionManager {
53
54     private static final Logger LOG = LoggerFactory.getLogger(ConnectionManagerImpl.class);
55     private static final boolean BITMAP_NEGOTIATION_ENABLED = true;
56     private final OpenflowProviderConfig config;
57     private final ExecutorService executorService;
58     private final DeviceConnectionRateLimiter deviceConnectionRateLimiter;
59     private final DataBroker dataBroker;
60     private final int deviceConnectionHoldTime;
61     private DeviceConnectedHandler deviceConnectedHandler;
62     private DeviceDisconnectedHandler deviceDisconnectedHandler;
63     private DeviceConnectionStatusProvider deviceConnectionStatusProvider;
64
65     public ConnectionManagerImpl(final OpenflowProviderConfig config, final ExecutorService executorService,
66                                  final DataBroker dataBroker) {
67         this.config = config;
68         this.executorService = executorService;
69         this.deviceConnectionRateLimiter = new DeviceConnectionRateLimiter(config);
70         this.dataBroker = dataBroker;
71         this.deviceConnectionHoldTime = config.getDeviceConnectionHoldTimeInSeconds().toJava();
72         deviceConnectionStatusProvider = new DeviceConnectionStatusProviderImpl();
73         deviceConnectionStatusProvider.init();
74     }
75
76     @Override
77     public void onSwitchConnected(final ConnectionAdapter connectionAdapter) {
78         LOG.trace("prepare connection context");
79         final ConnectionContext connectionContext = new ConnectionContextImpl(connectionAdapter,
80                 deviceConnectionStatusProvider);
81         connectionContext.setDeviceDisconnectedHandler(this.deviceDisconnectedHandler);
82
83         HandshakeListener handshakeListener = new HandshakeListenerImpl(connectionContext, deviceConnectedHandler);
84         final HandshakeManager handshakeManager = createHandshakeManager(connectionAdapter, handshakeListener);
85
86         LOG.trace("prepare handshake context");
87         HandshakeContext handshakeContext = new HandshakeContextImpl(executorService, handshakeManager);
88         handshakeListener.setHandshakeContext(handshakeContext);
89         connectionContext.setHandshakeContext(handshakeContext);
90
91         LOG.trace("prepare connection listeners");
92         final ConnectionReadyListener connectionReadyListener = new ConnectionReadyListenerImpl(
93                 connectionContext, handshakeContext);
94         connectionAdapter.setConnectionReadyListener(connectionReadyListener);
95
96         final OpenflowProtocolListener ofMessageListener =
97                 new OpenflowProtocolListenerInitialImpl(connectionContext, handshakeContext);
98         connectionAdapter.setMessageListener(ofMessageListener);
99
100         final SystemNotificationsListener systemListener = new SystemNotificationsListenerImpl(
101                 connectionContext, config.getEchoReplyTimeout().getValue().toJava(), executorService);
102         connectionAdapter.setSystemListener(systemListener);
103
104         LOG.trace("connection ballet finished");
105     }
106
107     private HandshakeManager createHandshakeManager(final ConnectionAdapter connectionAdapter,
108                                                     final HandshakeListener handshakeListener) {
109         HandshakeManagerImpl handshakeManager = new HandshakeManagerImpl(connectionAdapter,
110                 OFConstants.VERSION_ORDER.get(0),
111                 OFConstants.VERSION_ORDER, new ErrorHandlerSimpleImpl(), handshakeListener, BITMAP_NEGOTIATION_ENABLED,
112                 deviceConnectionRateLimiter, deviceConnectionHoldTime, deviceConnectionStatusProvider);
113
114         return handshakeManager;
115     }
116
117     @Override
118     public boolean accept(final InetAddress switchAddress) {
119         // TODO add connection accept logic based on address
120         return true;
121     }
122
123     @Override
124     public void setDeviceConnectedHandler(final DeviceConnectedHandler deviceConnectedHandler) {
125         this.deviceConnectedHandler = deviceConnectedHandler;
126     }
127
128     @Override
129     public void setDeviceDisconnectedHandler(final DeviceDisconnectedHandler deviceDisconnectedHandler) {
130         this.deviceDisconnectedHandler = deviceDisconnectedHandler;
131     }
132
133     @VisibleForTesting
134     DeviceConnectionStatusProvider getDeviceConnectionStatusProvider() {
135         return deviceConnectionStatusProvider;
136     }
137
138     @Override
139     public void close() throws Exception {
140         if (deviceConnectionStatusProvider != null) {
141             deviceConnectionStatusProvider.close();
142             deviceConnectionStatusProvider = null;
143         }
144     }
145
146     class DeviceConnectionStatusProviderImpl implements DeviceConnectionStatusProvider,
147             ClusteredDataTreeChangeListener<Node> {
148         private final Map<BigInteger, LocalDateTime> deviceConnectionMap = new ConcurrentHashMap<>();
149
150         private ListenerRegistration<DeviceConnectionStatusProviderImpl> listenerRegistration;
151
152         @Override
153         @SuppressWarnings({"checkstyle:IllegalCatch"})
154         public void init() {
155             DataTreeIdentifier<Node> treeId = DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL,
156                     getWildCardPath());
157             try {
158                 listenerRegistration = dataBroker.registerDataTreeChangeListener(treeId, this);
159             } catch (Exception e) {
160                 LOG.error("DeviceConnectionStatusProvider listener registration failed", e);
161             }
162         }
163
164         @Override
165         public LocalDateTime getDeviceLastConnectionTime(BigInteger nodeId) {
166             return deviceConnectionMap.get(nodeId);
167         }
168
169         @Override
170         public void addDeviceLastConnectionTime(BigInteger nodeId, LocalDateTime time) {
171             deviceConnectionMap.put(nodeId, time);
172         }
173
174         @Override
175         public void removeDeviceLastConnectionTime(BigInteger nodeId) {
176             deviceConnectionMap.remove(nodeId);
177         }
178
179         @Override
180         public void onDataTreeChanged(@NonNull Collection<DataTreeModification<Node>> changes) {
181             Preconditions.checkNotNull(changes, "Changes must not be null!");
182             for (DataTreeModification<Node> change : changes) {
183                 final DataObjectModification<Node> mod = change.getRootNode();
184                 switch (mod.getModificationType()) {
185                     case DELETE:
186                         break;
187                     case SUBTREE_MODIFIED:
188                         break;
189                     case WRITE:
190                         processNodeModification(change);
191                         break;
192                     default:
193                         throw new IllegalArgumentException("Unhandled modification type " + mod.getModificationType());
194                 }
195             }
196         }
197
198         private InstanceIdentifier<Node> getWildCardPath() {
199             return InstanceIdentifier.create(Nodes.class).child(Node.class);
200         }
201
202         private void processNodeModification(DataTreeModification<Node> change) {
203             final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
204             final InstanceIdentifier<Node> nodeIdent = key.firstIdentifierOf(Node.class);
205             String[] nodeIdentity = nodeIdent.firstKeyOf(Node.class).getId().getValue().split(":");
206             String nodeId = nodeIdentity[1];
207             LOG.info("Clearing the device connection timer for the device {}", nodeId);
208             removeDeviceLastConnectionTime(new BigInteger(nodeId));
209         }
210
211         @Override
212         public void close() {
213             if (listenerRegistration != null) {
214                 listenerRegistration.close();
215                 listenerRegistration = null;
216             }
217         }
218     }
219
220 }