blob: 51e8913743eb8384d23aa19e08d5b60e1ca0d131 [file] [log] [blame]
andreaeb70a942015-10-16 21:34:46 -07001/*
gyewan.an3c99ee72019-02-18 15:53:55 +09002 * Copyright 2019-present Open Networking Foundation
andreaeb70a942015-10-16 21:34:46 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Yuta HIGUCHIe3ae8212017-04-20 10:18:41 -070017package org.onosproject.netconf.ctl.impl;
andreaeb70a942015-10-16 21:34:46 -070018
gyewan.an91d7e7e2019-01-17 15:12:48 +090019
David K. Bainbridge56e90232018-12-18 23:25:08 -080020import org.apache.commons.lang3.tuple.Triple;
21
gyewan.an91d7e7e2019-01-17 15:12:48 +090022import com.google.common.annotations.Beta;
23import com.google.common.collect.Lists;
24
gyewan.an3c99ee72019-02-18 15:53:55 +090025import org.onosproject.cluster.ClusterService;
26import org.onosproject.cluster.ControllerNode;
27import org.onosproject.cluster.NodeId;
gyewan.an91d7e7e2019-01-17 15:12:48 +090028import org.osgi.service.component.annotations.Activate;
29import org.osgi.service.component.annotations.Component;
30import org.osgi.service.component.annotations.Deactivate;
31import org.osgi.service.component.annotations.Modified;
32import org.osgi.service.component.annotations.Reference;
33import org.osgi.service.component.annotations.ReferenceCardinality;
34
Holger Schulz092cbbf2017-08-31 17:52:30 +020035import org.bouncycastle.jce.provider.BouncyCastleProvider;
andreaeb70a942015-10-16 21:34:46 -070036import org.onlab.packet.IpAddress;
gyewan.an91d7e7e2019-01-17 15:12:48 +090037import org.onlab.util.KryoNamespace;
Andreas Papazois4752cfa2016-04-25 14:52:12 +030038import org.onosproject.cfg.ComponentConfigService;
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +010039import org.onosproject.mastership.MastershipService;
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -060040import org.onosproject.net.AnnotationKeys;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070041import org.onosproject.net.Device;
andreaeb70a942015-10-16 21:34:46 -070042import org.onosproject.net.DeviceId;
gyewan.an91d7e7e2019-01-17 15:12:48 +090043import org.onosproject.net.MastershipRole;
Sean Condon54d82432017-07-26 22:27:25 +010044import org.onosproject.net.config.NetworkConfigRegistry;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070045import org.onosproject.net.device.DeviceService;
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -060046import org.onosproject.net.key.DeviceKey;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070047import org.onosproject.net.key.DeviceKeyId;
48import org.onosproject.net.key.DeviceKeyService;
49import org.onosproject.net.key.UsernamePassword;
andreaeb70a942015-10-16 21:34:46 -070050import org.onosproject.netconf.NetconfController;
51import org.onosproject.netconf.NetconfDevice;
Andrea Campanella950310c2016-02-12 18:14:38 -080052import org.onosproject.netconf.NetconfDeviceFactory;
andreaeb70a942015-10-16 21:34:46 -070053import org.onosproject.netconf.NetconfDeviceInfo;
54import org.onosproject.netconf.NetconfDeviceListener;
Andrea Campanella2464dc32016-02-17 17:54:53 -080055import org.onosproject.netconf.NetconfDeviceOutputEvent;
56import org.onosproject.netconf.NetconfDeviceOutputEventListener;
Andrea Campanella101417d2015-12-11 17:58:07 -080057import org.onosproject.netconf.NetconfException;
gyewan.an91d7e7e2019-01-17 15:12:48 +090058import org.onosproject.netconf.NetconfProxyMessage;
59import org.onosproject.netconf.NetconfProxyMessageHandler;
60import org.onosproject.netconf.NetconfSession;
Sean Condon54d82432017-07-26 22:27:25 +010061import org.onosproject.netconf.config.NetconfDeviceConfig;
62import org.onosproject.netconf.config.NetconfSshClientLib;
gyewan.an91d7e7e2019-01-17 15:12:48 +090063import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
64import org.onosproject.store.cluster.messaging.MessageSubject;
65import org.onosproject.store.serializers.KryoNamespaces;
66import org.onosproject.store.service.Serializer;
andreaeb70a942015-10-16 21:34:46 -070067import org.osgi.service.component.ComponentContext;
gyewan.an91d7e7e2019-01-17 15:12:48 +090068
andreaeb70a942015-10-16 21:34:46 -070069import org.slf4j.Logger;
70import org.slf4j.LoggerFactory;
71
Holger Schulz092cbbf2017-08-31 17:52:30 +020072import java.security.Security;
gyewan.an91d7e7e2019-01-17 15:12:48 +090073
74import java.util.ArrayList;
Andreas Papazois4752cfa2016-04-25 14:52:12 +030075import java.util.Dictionary;
gyewan.an3c99ee72019-02-18 15:53:55 +090076import java.util.LinkedHashSet;
andreaeb70a942015-10-16 21:34:46 -070077import java.util.Map;
David K. Bainbridge56e90232018-12-18 23:25:08 -080078import java.util.Optional;
andreaeb70a942015-10-16 21:34:46 -070079import java.util.Set;
gyewan.an91d7e7e2019-01-17 15:12:48 +090080import java.util.concurrent.CompletableFuture;
andreaeb70a942015-10-16 21:34:46 -070081import java.util.concurrent.ConcurrentHashMap;
82import java.util.concurrent.CopyOnWriteArraySet;
gyewan.an3c99ee72019-02-18 15:53:55 +090083import java.util.concurrent.CountDownLatch;
gyewan.an91d7e7e2019-01-17 15:12:48 +090084import java.util.concurrent.ExecutionException;
Andrea Campanellac3627842017-04-04 18:06:54 +020085import java.util.concurrent.ExecutorService;
86import java.util.concurrent.Executors;
David K. Bainbridge9b582b02019-02-01 16:04:05 -080087import java.util.concurrent.locks.Lock;
88import java.util.concurrent.locks.ReentrantLock;
gyewan.an91d7e7e2019-01-17 15:12:48 +090089import java.util.concurrent.TimeUnit;
90import java.util.concurrent.TimeoutException;
andreaeb70a942015-10-16 21:34:46 -070091
Andreas Papazois4752cfa2016-04-25 14:52:12 +030092import static org.onlab.util.Tools.get;
Sean Condon54d82432017-07-26 22:27:25 +010093import static org.onlab.util.Tools.getIntegerProperty;
Andrea Campanellac3627842017-04-04 18:06:54 +020094import static org.onlab.util.Tools.groupedThreads;
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -070095import static org.onosproject.netconf.ctl.impl.OsgiPropertyConstants.*;
David K. Bainbridge56e90232018-12-18 23:25:08 -080096import static org.onosproject.netconf.NetconfDeviceInfo.extractIpPortPath;
Andreas Papazois4752cfa2016-04-25 14:52:12 +030097
andreaeb70a942015-10-16 21:34:46 -070098/**
99 * The implementation of NetconfController.
100 */
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -0700101@Component(immediate = true, service = NetconfController.class,
102 property = {
103 NETCONF_CONNECT_TIMEOUT + ":Integer=" + NETCONF_CONNECT_TIMEOUT_DEFAULT,
104 NETCONF_REPLY_TIMEOUT + ":Integer=" + NETCONF_REPLY_TIMEOUT_DEFAULT,
105 NETCONF_IDLE_TIMEOUT + ":Integer=" + NETCONF_IDLE_TIMEOUT_DEFAULT,
106 SSH_LIBRARY + "=" + SSH_LIBRARY_DEFAULT,
Anurag Chadha46732372021-05-10 17:32:59 +0530107 SSH_KEY_PATH + "=" + SSH_KEY_PATH_DEFAULT,
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -0700108 })
andreaeb70a942015-10-16 21:34:46 -0700109public class NetconfControllerImpl implements NetconfController {
Andrea Campanella7bbe7b12017-05-03 16:03:38 -0700110
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -0700111 /** Time (in seconds) to wait for a NETCONF connect. */
112 protected static int netconfConnectTimeout = NETCONF_CONNECT_TIMEOUT_DEFAULT;
Sean Condon334ad692016-12-13 17:56:56 +0000113
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -0700114 /** Time (in seconds) waiting for a NetConf reply. */
115 protected static int netconfReplyTimeout = NETCONF_REPLY_TIMEOUT_DEFAULT;
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300116
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -0700117 /** Time (in seconds) SSH session will close if no traffic seen. */
118 protected static int netconfIdleTimeout = NETCONF_IDLE_TIMEOUT_DEFAULT;
Sean Condon7347de92017-07-21 12:17:25 +0100119
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -0700120 /** SSH client library to use. */
121 protected static String sshLibrary = SSH_LIBRARY_DEFAULT;
122
Anurag Chadha46732372021-05-10 17:32:59 +0530123 /** Private SSH Key File Path to use. */
124 protected static String sshKeyPath = SSH_KEY_PATH_DEFAULT;
125
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -0700126 protected NetconfSshClientLib sshClientLib = NetconfSshClientLib.APACHE_MINA;
Andrea Campanella7bbe7b12017-05-03 16:03:38 -0700127
gyewan.an91d7e7e2019-01-17 15:12:48 +0900128 private static final MessageSubject SEND_REQUEST_SUBJECT_STRING =
129 new MessageSubject("netconf-session-master-send-message-string");
130
gyewan.an3c99ee72019-02-18 15:53:55 +0900131 private static final MessageSubject SEND_REPLY_SUBJECT_STRING =
132 new MessageSubject("netconf-session-master-send-reply-message-string");
133
gyewan.an91d7e7e2019-01-17 15:12:48 +0900134 private static final MessageSubject SEND_REQUEST_SUBJECT_SET_STRING =
135 new MessageSubject("netconf-session-master-send-message-set-string");
136
gyewan.an3c99ee72019-02-18 15:53:55 +0900137 private static final MessageSubject SEND_REPLY_SUBJECT_SET_STRING =
138 new MessageSubject("netconf-session-master-send-reply-message-set-string");
139
140
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700141 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300142 protected ComponentConfigService cfgService;
143
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700144 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700145 protected DeviceService deviceService;
146
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700147 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700148 protected DeviceKeyService deviceKeyService;
andreaeb70a942015-10-16 21:34:46 -0700149
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700150 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Sean Condon54d82432017-07-26 22:27:25 +0100151 protected NetworkConfigRegistry netCfgService;
152
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100153 @Reference(cardinality = ReferenceCardinality.MANDATORY)
154 protected MastershipService mastershipService;
155
gyewan.an91d7e7e2019-01-17 15:12:48 +0900156 @Reference(cardinality = ReferenceCardinality.MANDATORY)
157 protected ClusterCommunicationService clusterCommunicator;
158
gyewan.an3c99ee72019-02-18 15:53:55 +0900159 @Reference(cardinality = ReferenceCardinality.MANDATORY)
160 protected ClusterService clusterService;
161
andreaeb70a942015-10-16 21:34:46 -0700162 public static final Logger log = LoggerFactory
163 .getLogger(NetconfControllerImpl.class);
164
Andrea Campanella8b1cb672016-01-25 13:58:58 -0800165 private Map<DeviceId, NetconfDevice> netconfDeviceMap = new ConcurrentHashMap<>();
andreaeb70a942015-10-16 21:34:46 -0700166
David K. Bainbridge9b582b02019-02-01 16:04:05 -0800167 private Map<DeviceId, Lock> netconfCreateMutex = new ConcurrentHashMap<>();
168
Andrea Campanella2464dc32016-02-17 17:54:53 -0800169 private final NetconfDeviceOutputEventListener downListener = new DeviceDownEventListener();
170
andreaeb70a942015-10-16 21:34:46 -0700171 protected Set<NetconfDeviceListener> netconfDeviceListeners = new CopyOnWriteArraySet<>();
gyewan.an91d7e7e2019-01-17 15:12:48 +0900172 protected NetconfDeviceFactory deviceFactory = new DefaultNetconfDeviceFactory();
173
174 protected NetconfProxyMessageHandler netconfProxyMessageHandler = new NetconfProxyMessageHandlerImpl();
175
andreaeb70a942015-10-16 21:34:46 -0700176
Andrea Campanellac3627842017-04-04 18:06:54 +0200177 protected final ExecutorService executor =
178 Executors.newCachedThreadPool(groupedThreads("onos/netconfdevicecontroller",
Andrea Campanella7bbe7b12017-05-03 16:03:38 -0700179 "connection-reopen-%d", log));
Andrea Campanellac3627842017-04-04 18:06:54 +0200180
gyewan.an3c99ee72019-02-18 15:53:55 +0900181 private final ExecutorService remoteRequestExecutor =
182 Executors.newCachedThreadPool();
183
184 protected NodeId localNodeId;
185
186 private CountDownLatch countDownLatch;
187
188 private ArrayList<String> replyArguments = new ArrayList<>();
189
gyewan.an91d7e7e2019-01-17 15:12:48 +0900190 public static final Serializer SERIALIZER = Serializer.using(
191 KryoNamespace.newBuilder()
192 .register(KryoNamespaces.API)
193 .register(NetconfProxyMessage.class)
194 .register(NetconfProxyMessage.SubjectType.class)
195 .register(DefaultNetconfProxyMessage.class)
196 .register(String.class)
197 .build("NetconfProxySession"));
198
andreaeb70a942015-10-16 21:34:46 -0700199 @Activate
200 public void activate(ComponentContext context) {
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300201 cfgService.registerProperties(getClass());
202 modified(context);
Yuta HIGUCHIe9761742017-09-10 15:10:19 -0700203 Security.addProvider(new BouncyCastleProvider());
gyewan.an3c99ee72019-02-18 15:53:55 +0900204 clusterCommunicator.<NetconfProxyMessage>addSubscriber(
gyewan.an91d7e7e2019-01-17 15:12:48 +0900205 SEND_REQUEST_SUBJECT_STRING,
206 SERIALIZER::decode,
207 this::handleProxyMessage,
gyewan.an3c99ee72019-02-18 15:53:55 +0900208 remoteRequestExecutor);
209 clusterCommunicator.<NetconfProxyMessage>addSubscriber(
gyewan.an91d7e7e2019-01-17 15:12:48 +0900210 SEND_REQUEST_SUBJECT_SET_STRING,
211 SERIALIZER::decode,
212 this::handleProxyMessage,
gyewan.an3c99ee72019-02-18 15:53:55 +0900213 remoteRequestExecutor);
214 clusterCommunicator.<NetconfProxyMessage>addSubscriber(
215 SEND_REPLY_SUBJECT_STRING,
216 SERIALIZER::decode,
217 this::handleProxyReplyMessage,
218 remoteRequestExecutor);
219 clusterCommunicator.<NetconfProxyMessage>addSubscriber(
220 SEND_REPLY_SUBJECT_SET_STRING,
221 SERIALIZER::decode,
222 this::handleProxyReplyMessage,
223 remoteRequestExecutor);
224
225 localNodeId = Optional.ofNullable(clusterService.getLocalNode())
226 .map(ControllerNode::id)
227 .orElseGet(() -> new NodeId("nullNodeId"));
andreaeb70a942015-10-16 21:34:46 -0700228 log.info("Started");
229 }
230
231 @Deactivate
232 public void deactivate() {
Andrea Campanella7bbe7b12017-05-03 16:03:38 -0700233 netconfDeviceMap.values().forEach(device -> {
gyewan.an91d7e7e2019-01-17 15:12:48 +0900234 if (device.isMasterSession()) {
235 try {
236 device.getSession().removeDeviceOutputListener(downListener);
237 } catch (NetconfException e) {
238 log.error("removeDeviceOutputListener Failed {}", e.getMessage());
239 }
240 }
Andrea Campanella7bbe7b12017-05-03 16:03:38 -0700241 device.disconnect();
242 });
gyewan.an91d7e7e2019-01-17 15:12:48 +0900243 clusterCommunicator.removeSubscriber(SEND_REQUEST_SUBJECT_STRING);
244 clusterCommunicator.removeSubscriber(SEND_REQUEST_SUBJECT_SET_STRING);
gyewan.an3c99ee72019-02-18 15:53:55 +0900245 clusterCommunicator.removeSubscriber(SEND_REPLY_SUBJECT_STRING);
246 clusterCommunicator.removeSubscriber(SEND_REPLY_SUBJECT_SET_STRING);
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300247 cfgService.unregisterProperties(getClass(), false);
Andrea Campanella7bbe7b12017-05-03 16:03:38 -0700248 netconfDeviceListeners.clear();
andreaeb70a942015-10-16 21:34:46 -0700249 netconfDeviceMap.clear();
Holger Schulz092cbbf2017-08-31 17:52:30 +0200250 Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME);
andreaeb70a942015-10-16 21:34:46 -0700251 log.info("Stopped");
252 }
253
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300254 @Modified
255 public void modified(ComponentContext context) {
256 if (context == null) {
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -0700257 netconfReplyTimeout = NETCONF_REPLY_TIMEOUT_DEFAULT;
258 netconfConnectTimeout = NETCONF_CONNECT_TIMEOUT_DEFAULT;
259 netconfIdleTimeout = NETCONF_IDLE_TIMEOUT_DEFAULT;
260 sshLibrary = SSH_LIBRARY_DEFAULT;
Anurag Chadha46732372021-05-10 17:32:59 +0530261 sshKeyPath = SSH_KEY_PATH_DEFAULT;
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -0700262 sshClientLib = NetconfSshClientLib.APACHE_MINA;
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300263 log.info("No component configuration");
264 return;
265 }
266
267 Dictionary<?, ?> properties = context.getProperties();
268
Andrea Campanella7bbe7b12017-05-03 16:03:38 -0700269 String newSshLibrary;
Anurag Chadha46732372021-05-10 17:32:59 +0530270 String newSshKeyPath;
Sean Condon334ad692016-12-13 17:56:56 +0000271
Sean Condon54d82432017-07-26 22:27:25 +0100272 int newNetconfReplyTimeout = getIntegerProperty(
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -0700273 properties, NETCONF_REPLY_TIMEOUT, netconfReplyTimeout);
Sean Condon54d82432017-07-26 22:27:25 +0100274 int newNetconfConnectTimeout = getIntegerProperty(
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -0700275 properties, NETCONF_CONNECT_TIMEOUT, netconfConnectTimeout);
Sean Condon54d82432017-07-26 22:27:25 +0100276 int newNetconfIdleTimeout = getIntegerProperty(
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -0700277 properties, NETCONF_IDLE_TIMEOUT, netconfIdleTimeout);
Sean Condon334ad692016-12-13 17:56:56 +0000278
Sean Condon54d82432017-07-26 22:27:25 +0100279 newSshLibrary = get(properties, SSH_LIBRARY);
Anurag Chadha46732372021-05-10 17:32:59 +0530280 newSshKeyPath = get(properties, SSH_KEY_PATH);
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300281
Sean Condon334ad692016-12-13 17:56:56 +0000282 if (newNetconfConnectTimeout < 0) {
283 log.warn("netconfConnectTimeout is invalid - less than 0");
284 return;
285 } else if (newNetconfReplyTimeout <= 0) {
286 log.warn("netconfReplyTimeout is invalid - 0 or less.");
287 return;
Sean Condon7347de92017-07-21 12:17:25 +0100288 } else if (newNetconfIdleTimeout <= 0) {
289 log.warn("netconfIdleTimeout is invalid - 0 or less.");
290 return;
Sean Condon334ad692016-12-13 17:56:56 +0000291 }
292
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300293 netconfReplyTimeout = newNetconfReplyTimeout;
Sean Condon334ad692016-12-13 17:56:56 +0000294 netconfConnectTimeout = newNetconfConnectTimeout;
Sean Condon7347de92017-07-21 12:17:25 +0100295 netconfIdleTimeout = newNetconfIdleTimeout;
Sean Condon54d82432017-07-26 22:27:25 +0100296 if (newSshLibrary != null) {
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -0700297 sshLibrary = newSshLibrary;
298 sshClientLib = NetconfSshClientLib.getEnum(newSshLibrary);
Sean Condon54d82432017-07-26 22:27:25 +0100299 }
Anurag Chadha46732372021-05-10 17:32:59 +0530300 if (newSshKeyPath != null) {
301 sshKeyPath = newSshKeyPath;
302 }
303 log.info("Settings: {} = {}, {} = {}, {} = {}, {} = {}, {} = {}",
Thomas Vachuska00b5d4f2018-10-30 15:13:20 -0700304 NETCONF_REPLY_TIMEOUT, netconfReplyTimeout,
305 NETCONF_CONNECT_TIMEOUT, netconfConnectTimeout,
306 NETCONF_IDLE_TIMEOUT, netconfIdleTimeout,
Anurag Chadha46732372021-05-10 17:32:59 +0530307 SSH_LIBRARY, sshLibrary,
308 SSH_KEY_PATH, sshKeyPath);
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300309 }
310
andreaeb70a942015-10-16 21:34:46 -0700311 @Override
312 public void addDeviceListener(NetconfDeviceListener listener) {
313 if (!netconfDeviceListeners.contains(listener)) {
314 netconfDeviceListeners.add(listener);
315 }
316 }
317
318 @Override
319 public void removeDeviceListener(NetconfDeviceListener listener) {
320 netconfDeviceListeners.remove(listener);
321 }
322
323 @Override
324 public NetconfDevice getNetconfDevice(DeviceId deviceInfo) {
325 return netconfDeviceMap.get(deviceInfo);
326 }
327
328 @Override
David K. Bainbridge56e90232018-12-18 23:25:08 -0800329 public NetconfDevice getNetconfDevice(IpAddress ip, int port, String path) {
330 return getNetconfDevice(DeviceId.deviceId(
331 String.format("netconf:%s:%d%s",
332 ip.toString(), port, (path != null && !path.isEmpty() ? "/" + path : ""))));
333 }
334
335 @Override
andreaeb70a942015-10-16 21:34:46 -0700336 public NetconfDevice getNetconfDevice(IpAddress ip, int port) {
David K. Bainbridge56e90232018-12-18 23:25:08 -0800337 return getNetconfDevice(ip, port, null);
andreaeb70a942015-10-16 21:34:46 -0700338 }
339
340 @Override
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700341 public NetconfDevice connectDevice(DeviceId deviceId) throws NetconfException {
gyewan.an91d7e7e2019-01-17 15:12:48 +0900342 return connectDevice(deviceId, true);
343 }
344
345 @Override
346 public NetconfDevice connectDevice(DeviceId deviceId, boolean isMaster) throws NetconfException {
Sean Condon54d82432017-07-26 22:27:25 +0100347 NetconfDeviceConfig netCfg = netCfgService.getConfig(
348 deviceId, NetconfDeviceConfig.class);
349 NetconfDeviceInfo deviceInfo = null;
350
David K. Bainbridge9b582b02019-02-01 16:04:05 -0800351 /*
352 * A bit of an ugly race condition can be found here. It is possible
353 * that this method is called to create a connection to device A and
354 * while that device is in the process of being created another call
355 * to this method for A will be invoked. Since the first call to
356 * create A has not been completed device A is not in the the
357 * netconfDeviceMap yet.
358 *
359 * To prevent this situation a mutex is introduced so that the first
360 * call will be allowed to complete before the second is processed.
361 * The mutex is based on the device ID, so that it should be still
362 * possible to connect to different devices concurrently.
363 */
364 Lock mutex;
365 synchronized (netconfCreateMutex) {
366 mutex = netconfCreateMutex.get(deviceId);
367 if (mutex == null) {
368 mutex = new ReentrantLock();
369 netconfCreateMutex.put(deviceId, mutex);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700370 }
andreaeb70a942015-10-16 21:34:46 -0700371 }
David K. Bainbridge9b582b02019-02-01 16:04:05 -0800372 mutex.lock();
373 try {
374 if (netconfDeviceMap.containsKey(deviceId)) {
gyewan.an91d7e7e2019-01-17 15:12:48 +0900375 //If not master or already has session: return, otherwise create device again.
376 if (!isMaster || netconfDeviceMap.get(deviceId).isMasterSession()) {
377 log.debug("Device {} is already present", deviceId);
378 return netconfDeviceMap.get(deviceId);
379 }
380 }
381
382 if (netCfg != null) {
David K. Bainbridge9b582b02019-02-01 16:04:05 -0800383 log.debug("Device {} is present in NetworkConfig", deviceId);
Anurag Chadhab6a6bcb2021-03-26 14:43:41 +0530384 deviceInfo = new NetconfDeviceInfo(netCfg, deviceId);
David K. Bainbridge9b582b02019-02-01 16:04:05 -0800385 } else {
386 log.debug("Creating NETCONF device {}", deviceId);
gyewan.an91d7e7e2019-01-17 15:12:48 +0900387 deviceInfo = createDeviceInfo(deviceId);
David K. Bainbridge9b582b02019-02-01 16:04:05 -0800388 }
gyewan.an91d7e7e2019-01-17 15:12:48 +0900389 NetconfDevice netconfDevice = createDevice(deviceInfo, isMaster);
390 if (isMaster) {
391 netconfDevice.getSession().addDeviceOutputListener(downListener);
392 }
393 return netconfDevice;
David K. Bainbridge9b582b02019-02-01 16:04:05 -0800394 } finally {
Andrea Campanellac535b672019-02-25 16:25:35 +0100395
David K. Bainbridge9b582b02019-02-01 16:04:05 -0800396 mutex.unlock();
397 }
andreaeb70a942015-10-16 21:34:46 -0700398 }
399
gyewan.an3c99ee72019-02-18 15:53:55 +0900400 @Override
401 public NodeId getLocalNodeId() {
402 return localNodeId;
403 }
404
gyewan.an91d7e7e2019-01-17 15:12:48 +0900405 private NetconfDeviceInfo createDeviceInfo(DeviceId deviceId) throws NetconfException {
406 Device device = deviceService.getDevice(deviceId);
407 String ip, path = null;
408 int port;
409 if (device != null) {
410 ip = device.annotations().value("ipaddress");
411 port = Integer.parseInt(device.annotations().value("port"));
412 } else {
413 Triple<String, Integer, Optional<String>> info = extractIpPortPath(deviceId);
414 ip = info.getLeft();
415 port = info.getMiddle();
416 path = (info.getRight().isPresent() ? info.getRight().get() : null);
417 }
418 try {
419 DeviceKey deviceKey = deviceKeyService.getDeviceKey(
420 DeviceKeyId.deviceKeyId(deviceId.toString()));
421 if (deviceKey.type() == DeviceKey.Type.USERNAME_PASSWORD) {
422 UsernamePassword usernamepasswd = deviceKey.asUsernamePassword();
423
424 return new NetconfDeviceInfo(usernamepasswd.username(),
425 usernamepasswd.password(),
426 IpAddress.valueOf(ip),
427 port,
428 path);
429
430 } else if (deviceKey.type() == DeviceKey.Type.SSL_KEY) {
431 String username = deviceKey.annotations().value(AnnotationKeys.USERNAME);
432 String password = deviceKey.annotations().value(AnnotationKeys.PASSWORD);
433 String sshkey = deviceKey.annotations().value(AnnotationKeys.SSHKEY);
434
435 return new NetconfDeviceInfo(username,
436 password,
437 IpAddress.valueOf(ip),
438 port,
439 path,
440 sshkey);
441 } else {
442 log.error("Unknown device key for device {}", deviceId);
443 throw new NetconfException("Unknown device key for device " + deviceId);
444 }
445 } catch (NullPointerException e) {
446 log.error("No Device Key for device {}, {}", deviceId, e);
447 throw new NetconfException("No Device Key for device " + deviceId, e);
448 }
449 }
450
andreaeb70a942015-10-16 21:34:46 -0700451 @Override
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700452 public void disconnectDevice(DeviceId deviceId, boolean remove) {
453 if (!netconfDeviceMap.containsKey(deviceId)) {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100454 log.debug("Device {} is not present", deviceId);
andreaeb70a942015-10-16 21:34:46 -0700455 } else {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700456 stopDevice(deviceId, remove);
andreaeb70a942015-10-16 21:34:46 -0700457 }
458 }
459
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700460 private void stopDevice(DeviceId deviceId, boolean remove) {
David K. Bainbridge9b582b02019-02-01 16:04:05 -0800461 Lock mutex;
462 synchronized (netconfCreateMutex) {
463 mutex = netconfCreateMutex.remove(deviceId);
464 }
465 NetconfDevice nc;
466 if (mutex == null) {
467 log.warn("Unexpected stoping a device that has no lock");
468 nc = netconfDeviceMap.remove(deviceId);
469 } else {
470 mutex.lock();
471 try {
472 nc = netconfDeviceMap.remove(deviceId);
473 } finally {
474 mutex.unlock();
475 }
476 }
David K. Bainbridge1070de92019-01-29 22:23:11 -0800477 if (nc != null) {
478 nc.disconnect();
479 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700480 if (remove) {
Andrea Campanella86294db2016-03-07 11:42:49 -0800481 for (NetconfDeviceListener l : netconfDeviceListeners) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700482 l.deviceRemoved(deviceId);
483 }
484 }
485 }
486
487 @Override
488 public void removeDevice(DeviceId deviceId) {
489 if (!netconfDeviceMap.containsKey(deviceId)) {
490 log.warn("Device {} is not present", deviceId);
491 for (NetconfDeviceListener l : netconfDeviceListeners) {
492 l.deviceRemoved(deviceId);
493 }
494 } else {
zhongguo zhao78eab372018-08-27 16:22:39 +0800495 stopDevice(deviceId, true);
Andrea Campanella86294db2016-03-07 11:42:49 -0800496 }
497 }
498
Andrea Campanella101417d2015-12-11 17:58:07 -0800499 private NetconfDevice createDevice(NetconfDeviceInfo deviceInfo) throws NetconfException {
gyewan.an91d7e7e2019-01-17 15:12:48 +0900500 return createDevice(deviceInfo, true);
501 }
502
503 private NetconfDevice createDevice(NetconfDeviceInfo deviceInfo,
504 boolean isMaster) throws NetconfException {
505 NetconfDevice netconfDevice = deviceFactory.createNetconfDevice(deviceInfo, isMaster);
Andrea Campanella087ceb92015-12-07 09:58:34 -0800506 netconfDeviceMap.put(deviceInfo.getDeviceId(), netconfDevice);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700507 for (NetconfDeviceListener l : netconfDeviceListeners) {
508 l.deviceAdded(deviceInfo.getDeviceId());
509 }
andreaeb70a942015-10-16 21:34:46 -0700510 return netconfDevice;
511 }
512
andreaeb70a942015-10-16 21:34:46 -0700513
514 @Override
515 public Map<DeviceId, NetconfDevice> getDevicesMap() {
516 return netconfDeviceMap;
517 }
Andrea Campanella950310c2016-02-12 18:14:38 -0800518
Andrea Campanella86294db2016-03-07 11:42:49 -0800519 @Override
520 public Set<DeviceId> getNetconfDevices() {
521 return netconfDeviceMap.keySet();
522 }
523
gyewan.an3c99ee72019-02-18 15:53:55 +0900524 private void unicastRpcToMaster(NetconfProxyMessage proxyMessage, NodeId receiverId) {
525 MessageSubject messageSubject;
526
527 switch (proxyMessage.subjectType()) {
528 case GET_DEVICE_CAPABILITIES_SET:
529 messageSubject = SEND_REQUEST_SUBJECT_SET_STRING;
530 break;
531 default:
532 messageSubject = SEND_REQUEST_SUBJECT_STRING;
533 break;
534 }
535
536 clusterCommunicator
537 .unicast(proxyMessage,
538 messageSubject,
539 SERIALIZER::encode,
540 receiverId);
541 }
542
543 private void unicastReplyToSender(NetconfProxyMessage proxyMessage, NodeId receiverId) {
544 MessageSubject messageSubject;
545
546 switch (proxyMessage.subjectType()) {
547 case GET_DEVICE_CAPABILITIES_SET:
548 messageSubject = SEND_REPLY_SUBJECT_SET_STRING;
549 break;
550 default:
551 messageSubject = SEND_REPLY_SUBJECT_STRING;
552 break;
553 }
554
555 clusterCommunicator
556 .unicast(proxyMessage,
557 messageSubject,
558 SERIALIZER::encode,
559 receiverId);
560 }
561
gyewan.an91d7e7e2019-01-17 15:12:48 +0900562 @Override
563 public <T> CompletableFuture<T> executeAtMaster(NetconfProxyMessage proxyMessage) throws NetconfException {
564 DeviceId deviceId = proxyMessage.deviceId();
565 if (deviceService.getRole(deviceId).equals(MastershipRole.MASTER)) {
gyewan.an3c99ee72019-02-18 15:53:55 +0900566 return handleProxyMessage(proxyMessage);
gyewan.an91d7e7e2019-01-17 15:12:48 +0900567 } else {
gyewan.an3c99ee72019-02-18 15:53:55 +0900568 return relayMessageToMaster(proxyMessage);
569 }
570 }
571
Anurag Chadha924f01f2020-12-04 19:48:56 +0530572 @Override
573 public <T> boolean pingDevice(DeviceId deviceId) {
574 NetconfProxyMessage proxyMessage = new DefaultNetconfProxyMessage(
575 NetconfProxyMessage.SubjectType.GET_DEVICE_CAPABILITIES_SET, deviceId, null, localNodeId);
576 CompletableFuture<T> reply;
577 if (deviceService.getRole(deviceId).equals(MastershipRole.MASTER)) {
578 reply = handleProxyMessage(proxyMessage);
579 } else {
580 reply = relayMessageToMaster(proxyMessage);
581 }
582 try {
583 T deviceCapabilities = reply.get();
584 log.debug("Get device capabilities from device : {} -> {}", deviceId, deviceCapabilities);
585 } catch (InterruptedException | ExecutionException e) {
586 log.error("Error while getting device capabilities for device : {}", deviceId);
587 log.error("Error details : ", e);
588 return false;
589 }
590 return true;
591 }
592
gyewan.an3c99ee72019-02-18 15:53:55 +0900593 public <T> CompletableFuture<T> relayMessageToMaster(NetconfProxyMessage proxyMessage) {
594 DeviceId deviceId = proxyMessage.deviceId();
595
596 countDownLatch = new CountDownLatch(1);
597 unicastRpcToMaster(proxyMessage, mastershipService.getMasterFor(deviceId));
598
599 try {
600 countDownLatch.await(netconfReplyTimeout, TimeUnit.SECONDS);
601
gyewan.an91d7e7e2019-01-17 15:12:48 +0900602 switch (proxyMessage.subjectType()) {
603 case GET_DEVICE_CAPABILITIES_SET:
gyewan.an3c99ee72019-02-18 15:53:55 +0900604 Set<String> forReturnValue = new LinkedHashSet<>(replyArguments);
605 return CompletableFuture.completedFuture((T) forReturnValue);
gyewan.an91d7e7e2019-01-17 15:12:48 +0900606 default:
Anurag Chadha01ab40a2020-07-13 13:38:08 +0530607 String returnValue = null;
608 if (!replyArguments.isEmpty()) {
609 returnValue = Optional.ofNullable(replyArguments.get(0)).orElse(null);
610 }
gyewan.an3c99ee72019-02-18 15:53:55 +0900611 return CompletableFuture.completedFuture((T) returnValue);
gyewan.an91d7e7e2019-01-17 15:12:48 +0900612 }
gyewan.an3c99ee72019-02-18 15:53:55 +0900613 } catch (InterruptedException e) {
614 log.error("InterruptedOccured while awaiting because of {}", e);
615 CompletableFuture<T> errorFuture = new CompletableFuture<>();
616 errorFuture.completeExceptionally(e);
617 return errorFuture;
618 } catch (Exception e) {
619 log.error("Exception occured because of {}", e);
620 CompletableFuture<T> errorFuture = new CompletableFuture<>();
621 errorFuture.completeExceptionally(e);
622 return errorFuture;
gyewan.an91d7e7e2019-01-17 15:12:48 +0900623 }
624 }
625
626 private <T> CompletableFuture<T> handleProxyMessage(NetconfProxyMessage proxyMessage) {
Anurag Chadha924f01f2020-12-04 19:48:56 +0530627 countDownLatch = new CountDownLatch(1);
gyewan.an91d7e7e2019-01-17 15:12:48 +0900628 try {
gyewan.an3c99ee72019-02-18 15:53:55 +0900629 switch (proxyMessage.subjectType()) {
630 case GET_DEVICE_CAPABILITIES_SET:
631 return CompletableFuture.completedFuture(
632 (T) netconfProxyMessageHandler.handleIncomingSetMessage(proxyMessage));
633 default:
634 return CompletableFuture.completedFuture(
635 netconfProxyMessageHandler.handleIncomingMessage(proxyMessage));
636 }
gyewan.an91d7e7e2019-01-17 15:12:48 +0900637 } catch (NetconfException e) {
638 CompletableFuture<T> errorFuture = new CompletableFuture<>();
639 errorFuture.completeExceptionally(e);
640 return errorFuture;
641 }
642 }
643
gyewan.an3c99ee72019-02-18 15:53:55 +0900644 private <T> CompletableFuture<T> handleProxyReplyMessage(NetconfProxyMessage replyMessage) {
645 try {
646 switch (replyMessage.subjectType()) {
647 case GET_DEVICE_CAPABILITIES_SET:
648 return CompletableFuture.completedFuture(
649 (T) netconfProxyMessageHandler.handleReplySetMessage(replyMessage));
650 default:
651 return CompletableFuture.completedFuture(
652 netconfProxyMessageHandler.handleReplyMessage(replyMessage));
653
654 }
655 } catch (NetconfException e) {
656 CompletableFuture<T> errorFuture = new CompletableFuture<>();
657 errorFuture.completeExceptionally(e);
658 return errorFuture;
659 }
660 }
661
662
gyewan.an91d7e7e2019-01-17 15:12:48 +0900663 /**
664 * Netconf Proxy Message Handler Implementation class.
665 */
666 private class NetconfProxyMessageHandlerImpl implements NetconfProxyMessageHandler {
667
668 @Override
669 public <T> T handleIncomingMessage(NetconfProxyMessage proxyMessage) throws NetconfException {
670 //TODO: Should throw Netconf Exception in error cases?
671 DeviceId deviceId = proxyMessage.deviceId();
672 NetconfProxyMessage.SubjectType subjectType = proxyMessage.subjectType();
673 NetconfSession secureTransportSession;
gyewan.an3c99ee72019-02-18 15:53:55 +0900674
Anurag Chadha924f01f2020-12-04 19:48:56 +0530675 if (netconfDeviceMap.get(deviceId) != null && netconfDeviceMap.get(deviceId).isMasterSession()) {
gyewan.an91d7e7e2019-01-17 15:12:48 +0900676 secureTransportSession = netconfDeviceMap.get(deviceId).getSession();
677 } else {
678 throw new NetconfException("Ssh session not present");
679 }
680 T reply = null;
681 ArrayList<String> arguments = Lists.newArrayList(proxyMessage.arguments());
682 try {
683 switch (subjectType) {
684 case RPC:
685 reply = (T) secureTransportSession.rpc(arguments.get(0))
686 .get(netconfReplyTimeout, TimeUnit.SECONDS);
687 break;
688 case REQUEST_SYNC:
689 reply = (T) secureTransportSession.requestSync(arguments.get(0));
690 break;
691 case START_SUBSCRIPTION:
692 secureTransportSession.startSubscription(arguments.get(0));
693 break;
694 case END_SUBSCRIPTION:
695 secureTransportSession.endSubscription();
696 break;
697 case REQUEST:
698 reply = (T) secureTransportSession.request(arguments.get(0))
699 .get(netconfReplyTimeout, TimeUnit.SECONDS);
700 break;
701 case GET_SESSION_ID:
702 reply = (T) secureTransportSession.getSessionId();
703 break;
gyewan.an91d7e7e2019-01-17 15:12:48 +0900704 case GET_DEVICE_CAPABILITIES_SET:
705 reply = (T) secureTransportSession.getDeviceCapabilitiesSet();
706 break;
707 default:
708 log.error("Not yet supported for session method {}", subjectType);
709 }
710 } catch (InterruptedException e) {
711 Thread.currentThread().interrupt();
712 throw new NetconfException(e.getMessage(), e.getCause());
713 } catch (ExecutionException | TimeoutException e) {
714 throw new NetconfException(e.getMessage(), e.getCause());
715 }
716
gyewan.an3c99ee72019-02-18 15:53:55 +0900717 ArrayList<String> returnArgument = new ArrayList<String>();
718 Optional.ofNullable(reply).ifPresent(r -> returnArgument.add((String) r));
719
720 DefaultNetconfProxyMessage replyMessage = new DefaultNetconfProxyMessage(
721 subjectType,
722 deviceId,
723 returnArgument,
724 localNodeId);
725
726 unicastReplyToSender(replyMessage, proxyMessage.senderId());
727
728
gyewan.an91d7e7e2019-01-17 15:12:48 +0900729 return reply;
730 }
gyewan.an3c99ee72019-02-18 15:53:55 +0900731
732 @Override
733 public <T> T handleReplyMessage(NetconfProxyMessage replyMessage) {
734 replyArguments = new ArrayList<>(replyMessage.arguments());
735 countDownLatch.countDown();
736 return (T) Optional.ofNullable(replyArguments.get(0)).orElse(null);
737 }
738
739 @Override
740 public Set<String> handleIncomingSetMessage(NetconfProxyMessage proxyMessage) throws NetconfException {
741 DeviceId deviceId = proxyMessage.deviceId();
742 NetconfProxyMessage.SubjectType subjectType = proxyMessage.subjectType();
743 NetconfSession secureTransportSession;
744
Anurag Chadha924f01f2020-12-04 19:48:56 +0530745 if (netconfDeviceMap.get(deviceId) != null && netconfDeviceMap.get(deviceId).isMasterSession()) {
gyewan.an3c99ee72019-02-18 15:53:55 +0900746 secureTransportSession = netconfDeviceMap.get(deviceId).getSession();
747 } else {
748 throw new NetconfException("SSH session not present");
749 }
750
751 Set<String> reply = secureTransportSession.getDeviceCapabilitiesSet();
752 ArrayList<String> returnArgument = new ArrayList<String>(reply);
753
754 DefaultNetconfProxyMessage replyMessage = new DefaultNetconfProxyMessage(
755 subjectType,
756 deviceId,
757 returnArgument,
758 localNodeId);
759
760 unicastReplyToSender(replyMessage, proxyMessage.senderId());
761 return reply;
762 }
763
764 @Override
765 public Set<String> handleReplySetMessage(NetconfProxyMessage replyMessage) {
766 replyArguments = new ArrayList<>(replyMessage.arguments());
767 countDownLatch.countDown();
768
769 return new LinkedHashSet<>(replyArguments);
770
771 }
gyewan.an91d7e7e2019-01-17 15:12:48 +0900772 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700773
Yuta HIGUCHI2ee4fba2018-06-12 16:21:06 -0700774 /**
775 * Device factory for the specific NetconfDeviceImpl.
776 *
777 * @deprecated in 1.14.0
778 */
779 @Deprecated
Andrea Campanella950310c2016-02-12 18:14:38 -0800780 private class DefaultNetconfDeviceFactory implements NetconfDeviceFactory {
781
782 @Override
gyewan.an91d7e7e2019-01-17 15:12:48 +0900783 public NetconfDevice createNetconfDevice(NetconfDeviceInfo netconfDeviceInfo) throws NetconfException {
784 return createNetconfDevice(netconfDeviceInfo, true);
785 }
786
787 @Beta
788 @Override
789 public NetconfDevice createNetconfDevice(NetconfDeviceInfo netconfDeviceInfo,
790 boolean isMaster)
Andrea Campanella7bbe7b12017-05-03 16:03:38 -0700791 throws NetconfException {
gyewan.an91d7e7e2019-01-17 15:12:48 +0900792 if (isMaster) {
793 log.info("Creating NETCONF session to {} with {}",
794 netconfDeviceInfo.getDeviceId(), NetconfSshClientLib.APACHE_MINA);
795 }
796 return new DefaultNetconfDevice(netconfDeviceInfo, isMaster, NetconfControllerImpl.this);
Andrea Campanella950310c2016-02-12 18:14:38 -0800797 }
798 }
Andrea Campanella2464dc32016-02-17 17:54:53 -0800799
Yuta HIGUCHIf7089102017-05-03 10:31:46 -0700800 //Listener for closed session with devices, gets triggered when devices goes down
801 // or sends the end pattern ]]>]]>
Andrea Campanella2464dc32016-02-17 17:54:53 -0800802 private class DeviceDownEventListener implements NetconfDeviceOutputEventListener {
803
804 @Override
805 public void event(NetconfDeviceOutputEvent event) {
Andrea Campanellac3627842017-04-04 18:06:54 +0200806 DeviceId did = event.getDeviceInfo().getDeviceId();
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100807 if (event.type().equals(NetconfDeviceOutputEvent.Type.DEVICE_UNREGISTERED) ||
808 !mastershipService.isLocalMaster(did)) {
Andrea Campanellac3627842017-04-04 18:06:54 +0200809 removeDevice(did);
810 } else if (event.type().equals(NetconfDeviceOutputEvent.Type.SESSION_CLOSED)) {
811 log.info("Trying to reestablish connection with device {}", did);
812 executor.execute(() -> {
813 try {
Andrea Campanella7bbe7b12017-05-03 16:03:38 -0700814 NetconfDevice device = netconfDeviceMap.get(did);
815 if (device != null) {
816 device.getSession().checkAndReestablish();
817 log.info("Connection with device {} was reestablished", did);
818 } else {
819 log.warn("The device {} is not in the system", did);
820 }
821
Andrea Campanellac3627842017-04-04 18:06:54 +0200822 } catch (NetconfException e) {
823 log.error("The SSH connection with device {} couldn't be " +
Sean Condon54d82432017-07-26 22:27:25 +0100824 "reestablished due to {}. " +
Andrea Campanellac535b672019-02-25 16:25:35 +0100825 "Marking the device as unreachable", did, e.getMessage());
Andrea Campanellac3627842017-04-04 18:06:54 +0200826 log.debug("Complete exception: ", e);
827 removeDevice(did);
828 }
829 });
Andrea Campanella2464dc32016-02-17 17:54:53 -0800830 }
831 }
832
833 @Override
834 public boolean isRelevant(NetconfDeviceOutputEvent event) {
835 return getDevicesMap().containsKey(event.getDeviceInfo().getDeviceId());
836 }
837 }
andreaeb70a942015-10-16 21:34:46 -0700838}