5bbfc273b449a927d378c7a7002cba4d16742ba9
[openflowplugin.git] / openflowplugin-impl / src / main / java / org / opendaylight / openflowplugin / impl / connection / listener / SystemNotificationsListenerImpl.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.listener;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.annotations.VisibleForTesting;
13 import java.net.InetSocketAddress;
14 import java.util.Date;
15 import java.util.Objects;
16 import java.util.concurrent.Executor;
17 import java.util.concurrent.Future;
18 import java.util.concurrent.TimeUnit;
19 import org.eclipse.jdt.annotation.NonNull;
20 import org.opendaylight.mdsal.binding.api.NotificationPublishService;
21 import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
22 import org.opendaylight.openflowplugin.api.openflow.device.Xid;
23 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
24 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.node.ssl.connection.error.service.rev190723.SslErrorBuilder;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.node.ssl.connection.error.service.rev190723.SslErrorType;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.node.ssl.connection.error.service.rev190723.ssl.error.SwitchCertificateBuilder;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.EchoInputBuilder;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.EchoOutput;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FeaturesReply;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.system.rev130927.DisconnectEvent;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.system.rev130927.SslConnectionError;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.system.rev130927.SwitchIdleEvent;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.system.rev130927.SystemNotificationsListener;
35 import org.opendaylight.yangtools.yang.common.RpcError;
36 import org.opendaylight.yangtools.yang.common.RpcResult;
37 import org.opendaylight.yangtools.yang.common.Uint16;
38 import org.opendaylight.yangtools.yang.common.Uint32;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42 public class SystemNotificationsListenerImpl implements SystemNotificationsListener {
43
44     private static final Logger LOG = LoggerFactory.getLogger(SystemNotificationsListenerImpl.class);
45     private static final Logger OF_EVENT_LOG = LoggerFactory.getLogger("OfEventLog");
46     private static final Xid ECHO_XID = new Xid(Uint32.ZERO);
47
48     private final ConnectionContext connectionContext;
49     @VisibleForTesting
50     static final long MAX_ECHO_REPLY_TIMEOUT = 2000;
51     private final long echoReplyTimeout;
52     private final Executor executor;
53     private final NotificationPublishService notificationPublishService;
54
55     public SystemNotificationsListenerImpl(@NonNull final ConnectionContext connectionContext,
56                                            final long echoReplyTimeout,
57                                            @NonNull final Executor executor,
58                                            @NonNull final NotificationPublishService notificationPublishService) {
59         this.executor = requireNonNull(executor);
60         this.connectionContext = requireNonNull(connectionContext);
61         this.echoReplyTimeout = echoReplyTimeout;
62         this.notificationPublishService = notificationPublishService;
63     }
64
65     @Override
66     public void onDisconnectEvent(final DisconnectEvent notification) {
67         OF_EVENT_LOG.debug("Disconnect, Node: {}", connectionContext.getSafeNodeIdForLOG());
68         LOG.info("ConnectionEvent: Connection closed by device, Device:{}, NodeId:{}",
69                 connectionContext.getConnectionAdapter().getRemoteAddress(), connectionContext.getSafeNodeIdForLOG());
70         connectionContext.onConnectionClosed();
71     }
72
73     @Override
74     public void onSwitchIdleEvent(final SwitchIdleEvent notification) {
75         executor.execute(this::executeOnSwitchIdleEvent);
76     }
77
78     @SuppressWarnings("checkstyle:IllegalCatch")
79     private void executeOnSwitchIdleEvent() {
80         boolean shouldBeDisconnected = true;
81
82         final InetSocketAddress remoteAddress = connectionContext.getConnectionAdapter().getRemoteAddress();
83
84         if (ConnectionContext.CONNECTION_STATE.WORKING.equals(connectionContext.getConnectionState())) {
85             FeaturesReply features = connectionContext.getFeatures();
86             LOG.debug("Switch Idle state occurred, node={}|auxId={}", remoteAddress, features.getAuxiliaryId());
87             OF_EVENT_LOG.debug("Switch idle state, Node: {}", features.getDatapathId());
88             connectionContext.changeStateToTimeouting();
89             EchoInputBuilder builder = new EchoInputBuilder();
90             builder.setVersion(features.getVersion());
91             builder.setXid(ECHO_XID.getValue());
92
93             Future<RpcResult<EchoOutput>> echoReplyFuture =
94                     connectionContext.getConnectionAdapter().echo(builder.build());
95
96             try {
97                 RpcResult<EchoOutput> echoReplyValue = echoReplyFuture.get(echoReplyTimeout, TimeUnit.MILLISECONDS);
98                 if (echoReplyValue.isSuccessful()
99                         && Objects.equals(echoReplyValue.getResult().getXid(), ECHO_XID.getValue())) {
100                     connectionContext.changeStateToWorking();
101                     shouldBeDisconnected = false;
102                 } else {
103                     logErrors(remoteAddress, echoReplyValue);
104                 }
105             } catch (Exception e) {
106                 if (LOG.isWarnEnabled()) {
107                     LOG.warn("Exception while  waiting for echoReply from [{}] in TIMEOUTING state: {}",
108                             remoteAddress, e.getMessage());
109                 }
110
111                 if (LOG.isTraceEnabled()) {
112                     LOG.trace("Exception while  waiting for echoReply from [{}] in TIMEOUTING state",
113                             remoteAddress, e);
114                 }
115
116             }
117         }
118         if (shouldBeDisconnected) {
119             if (LOG.isInfoEnabled()) {
120                 LOG.info("ConnectionEvent:Closing connection as device is idle. Echo sent at {}. Device:{}, NodeId:{}",
121                         new Date(System.currentTimeMillis() - echoReplyTimeout),
122                         remoteAddress, connectionContext.getSafeNodeIdForLOG());
123             }
124
125             connectionContext.closeConnection(true);
126         }
127     }
128
129     private static void logErrors(final InetSocketAddress remoteAddress, final RpcResult<EchoOutput> echoReplyValue) {
130         for (RpcError replyError : echoReplyValue.getErrors()) {
131             Throwable cause = replyError.getCause();
132             if (LOG.isWarnEnabled()) {
133                 LOG.warn("Received EchoReply from [{}] in TIMEOUTING state, Error:{}",
134                         remoteAddress, cause.getMessage());
135             }
136
137             if (LOG.isTraceEnabled()) {
138                 LOG.trace("Received EchoReply from [{}] in TIMEOUTING state", remoteAddress, cause);
139             }
140         }
141     }
142
143     @Override
144     public void onSslConnectionError(final SslConnectionError notification) {
145         IpAddress ip = null;
146         if (connectionContext.getConnectionAdapter() != null
147                 && connectionContext.getConnectionAdapter().getRemoteAddress() != null
148                 && connectionContext.getConnectionAdapter().getRemoteAddress().getAddress() != null) {
149             ip = IpAddressBuilder.getDefaultInstance(
150                     connectionContext.getConnectionAdapter().getRemoteAddress().getAddress().getHostAddress());
151         }
152         SwitchCertificateBuilder switchCertificateBuilder = new SwitchCertificateBuilder();
153         if (notification.getSwitchCertificate() != null) {
154             switchCertificateBuilder = new SwitchCertificateBuilder(notification.getSwitchCertificate());
155         }
156         notificationPublishService.offerNotification(new SslErrorBuilder()
157             .setType(SslErrorType.SslConFailed)
158             .setCode(Uint16.valueOf(SslErrorType.SslConFailed.getIntValue()))
159             .setNodeIpAddress(ip)
160             .setData(notification.getInfo())
161             .setSwitchCertificate(notification.getSwitchCertificate() != null ? switchCertificateBuilder.build() : null)
162             .build());
163     }
164 }