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