blob: 2785fa33e22f1b9ac327bae4e5806e366a52c57c [file] [log] [blame]
andreaeb70a942015-10-16 21:34:46 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
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
19import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
Andreas Papazois4752cfa2016-04-25 14:52:12 +030022import org.apache.felix.scr.annotations.Modified;
23import org.apache.felix.scr.annotations.Property;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070024import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
andreaeb70a942015-10-16 21:34:46 -070026import org.apache.felix.scr.annotations.Service;
27import org.onlab.packet.IpAddress;
Andreas Papazois4752cfa2016-04-25 14:52:12 +030028import org.onosproject.cfg.ComponentConfigService;
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -060029import org.onosproject.net.AnnotationKeys;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070030import org.onosproject.net.Device;
andreaeb70a942015-10-16 21:34:46 -070031import org.onosproject.net.DeviceId;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070032import org.onosproject.net.device.DeviceService;
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -060033import org.onosproject.net.key.DeviceKey;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070034import org.onosproject.net.key.DeviceKeyId;
35import org.onosproject.net.key.DeviceKeyService;
36import org.onosproject.net.key.UsernamePassword;
andreaeb70a942015-10-16 21:34:46 -070037import org.onosproject.netconf.NetconfController;
38import org.onosproject.netconf.NetconfDevice;
Andrea Campanella950310c2016-02-12 18:14:38 -080039import org.onosproject.netconf.NetconfDeviceFactory;
andreaeb70a942015-10-16 21:34:46 -070040import org.onosproject.netconf.NetconfDeviceInfo;
41import org.onosproject.netconf.NetconfDeviceListener;
Andrea Campanella2464dc32016-02-17 17:54:53 -080042import org.onosproject.netconf.NetconfDeviceOutputEvent;
43import org.onosproject.netconf.NetconfDeviceOutputEventListener;
Andrea Campanella101417d2015-12-11 17:58:07 -080044import org.onosproject.netconf.NetconfException;
andreaeb70a942015-10-16 21:34:46 -070045import org.osgi.service.component.ComponentContext;
46import org.slf4j.Logger;
47import org.slf4j.LoggerFactory;
48
Andrea Campanella7e6200a2016-03-21 09:48:40 -070049import java.util.Arrays;
Andreas Papazois4752cfa2016-04-25 14:52:12 +030050import java.util.Dictionary;
andreaeb70a942015-10-16 21:34:46 -070051import java.util.Map;
52import java.util.Set;
53import java.util.concurrent.ConcurrentHashMap;
54import java.util.concurrent.CopyOnWriteArraySet;
Andrea Campanellac3627842017-04-04 18:06:54 +020055import java.util.concurrent.ExecutorService;
56import java.util.concurrent.Executors;
andreaeb70a942015-10-16 21:34:46 -070057
Andreas Papazois4752cfa2016-04-25 14:52:12 +030058import static com.google.common.base.Strings.isNullOrEmpty;
59import static org.onlab.util.Tools.get;
Andrea Campanellac3627842017-04-04 18:06:54 +020060import static org.onlab.util.Tools.groupedThreads;
Andreas Papazois4752cfa2016-04-25 14:52:12 +030061
andreaeb70a942015-10-16 21:34:46 -070062/**
63 * The implementation of NetconfController.
64 */
65@Component(immediate = true)
66@Service
67public class NetconfControllerImpl implements NetconfController {
Andrea Campanella7bbe7b12017-05-03 16:03:38 -070068
69 private static final String ETHZ_SSH2 = "ethz-ssh2";
70
Sean Condon334ad692016-12-13 17:56:56 +000071 private static final int DEFAULT_CONNECT_TIMEOUT_SECONDS = 5;
72 private static final String PROP_NETCONF_CONNECT_TIMEOUT = "netconfConnectTimeout";
73 @Property(name = PROP_NETCONF_CONNECT_TIMEOUT, intValue = DEFAULT_CONNECT_TIMEOUT_SECONDS,
74 label = "Time (in seconds) to wait for a NETCONF connect.")
75 protected static int netconfConnectTimeout = DEFAULT_CONNECT_TIMEOUT_SECONDS;
76
Andreas Papazois4752cfa2016-04-25 14:52:12 +030077 private static final String PROP_NETCONF_REPLY_TIMEOUT = "netconfReplyTimeout";
78 private static final int DEFAULT_REPLY_TIMEOUT_SECONDS = 5;
79 @Property(name = PROP_NETCONF_REPLY_TIMEOUT, intValue = DEFAULT_REPLY_TIMEOUT_SECONDS,
80 label = "Time (in seconds) waiting for a NetConf reply")
81 protected static int netconfReplyTimeout = DEFAULT_REPLY_TIMEOUT_SECONDS;
82
Sean Condon7347de92017-07-21 12:17:25 +010083 private static final String PROP_NETCONF_IDLE_TIMEOUT = "netconfIdleTimeout";
84 private static final int DEFAULT_IDLE_TIMEOUT_SECONDS = 300;
85 @Property(name = PROP_NETCONF_IDLE_TIMEOUT, intValue = DEFAULT_IDLE_TIMEOUT_SECONDS,
86 label = "Time (in seconds) SSH session will close if no traffic seen")
87 protected static int netconfIdleTimeout = DEFAULT_IDLE_TIMEOUT_SECONDS;
88
Andrea Campanella7bbe7b12017-05-03 16:03:38 -070089 private static final String SSH_LIBRARY = "sshLibrary";
90 private static final String APACHE_MINA = "apache_mina";
91 @Property(name = SSH_LIBRARY, value = APACHE_MINA,
Sean Condon7347de92017-07-21 12:17:25 +010092 label = "Ssh Library instead of apache_mina (i.e. ethz-ssh2")
Andrea Campanella7bbe7b12017-05-03 16:03:38 -070093 protected static String sshLibrary = APACHE_MINA;
94
Andreas Papazois4752cfa2016-04-25 14:52:12 +030095 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected ComponentConfigService cfgService;
97
Andrea Campanella7e6200a2016-03-21 09:48:40 -070098 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 protected DeviceService deviceService;
100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
102 protected DeviceKeyService deviceKeyService;
andreaeb70a942015-10-16 21:34:46 -0700103
104 public static final Logger log = LoggerFactory
105 .getLogger(NetconfControllerImpl.class);
106
Andrea Campanella8b1cb672016-01-25 13:58:58 -0800107 private Map<DeviceId, NetconfDevice> netconfDeviceMap = new ConcurrentHashMap<>();
andreaeb70a942015-10-16 21:34:46 -0700108
Andrea Campanella2464dc32016-02-17 17:54:53 -0800109 private final NetconfDeviceOutputEventListener downListener = new DeviceDownEventListener();
110
andreaeb70a942015-10-16 21:34:46 -0700111 protected Set<NetconfDeviceListener> netconfDeviceListeners = new CopyOnWriteArraySet<>();
Andrea Campanella950310c2016-02-12 18:14:38 -0800112 protected NetconfDeviceFactory deviceFactory = new DefaultNetconfDeviceFactory();
andreaeb70a942015-10-16 21:34:46 -0700113
Andrea Campanellac3627842017-04-04 18:06:54 +0200114 protected final ExecutorService executor =
115 Executors.newCachedThreadPool(groupedThreads("onos/netconfdevicecontroller",
Andrea Campanella7bbe7b12017-05-03 16:03:38 -0700116 "connection-reopen-%d", log));
Andrea Campanellac3627842017-04-04 18:06:54 +0200117
andreaeb70a942015-10-16 21:34:46 -0700118 @Activate
119 public void activate(ComponentContext context) {
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300120 cfgService.registerProperties(getClass());
121 modified(context);
andreaeb70a942015-10-16 21:34:46 -0700122 log.info("Started");
123 }
124
125 @Deactivate
126 public void deactivate() {
Andrea Campanella7bbe7b12017-05-03 16:03:38 -0700127 netconfDeviceMap.values().forEach(device -> {
128 device.getSession().removeDeviceOutputListener(downListener);
129 device.disconnect();
130 });
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300131 cfgService.unregisterProperties(getClass(), false);
Andrea Campanella7bbe7b12017-05-03 16:03:38 -0700132 netconfDeviceListeners.clear();
andreaeb70a942015-10-16 21:34:46 -0700133 netconfDeviceMap.clear();
134 log.info("Stopped");
135 }
136
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300137 @Modified
138 public void modified(ComponentContext context) {
139 if (context == null) {
140 netconfReplyTimeout = DEFAULT_REPLY_TIMEOUT_SECONDS;
Sean Condon334ad692016-12-13 17:56:56 +0000141 netconfConnectTimeout = DEFAULT_CONNECT_TIMEOUT_SECONDS;
Andrea Campanella7bbe7b12017-05-03 16:03:38 -0700142 sshLibrary = APACHE_MINA;
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300143 log.info("No component configuration");
144 return;
145 }
146
147 Dictionary<?, ?> properties = context.getProperties();
148
149 int newNetconfReplyTimeout;
Sean Condon334ad692016-12-13 17:56:56 +0000150 int newNetconfConnectTimeout;
Sean Condon7347de92017-07-21 12:17:25 +0100151 int newNetconfIdleTimeout;
Andrea Campanella7bbe7b12017-05-03 16:03:38 -0700152 String newSshLibrary;
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300153 try {
154 String s = get(properties, PROP_NETCONF_REPLY_TIMEOUT);
155 newNetconfReplyTimeout = isNullOrEmpty(s) ?
156 netconfReplyTimeout : Integer.parseInt(s.trim());
Sean Condon334ad692016-12-13 17:56:56 +0000157
158 s = get(properties, PROP_NETCONF_CONNECT_TIMEOUT);
159 newNetconfConnectTimeout = isNullOrEmpty(s) ?
160 netconfConnectTimeout : Integer.parseInt(s.trim());
161
Sean Condon7347de92017-07-21 12:17:25 +0100162 s = get(properties, PROP_NETCONF_IDLE_TIMEOUT);
163 newNetconfIdleTimeout = isNullOrEmpty(s) ?
164 netconfIdleTimeout : Integer.parseInt(s.trim());
165
Andrea Campanella7bbe7b12017-05-03 16:03:38 -0700166 newSshLibrary = get(properties, SSH_LIBRARY);
167
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300168 } catch (NumberFormatException e) {
169 log.warn("Component configuration had invalid value", e);
170 return;
171 }
172
Sean Condon334ad692016-12-13 17:56:56 +0000173 if (newNetconfConnectTimeout < 0) {
174 log.warn("netconfConnectTimeout is invalid - less than 0");
175 return;
176 } else if (newNetconfReplyTimeout <= 0) {
177 log.warn("netconfReplyTimeout is invalid - 0 or less.");
178 return;
Sean Condon7347de92017-07-21 12:17:25 +0100179 } else if (newNetconfIdleTimeout <= 0) {
180 log.warn("netconfIdleTimeout is invalid - 0 or less.");
181 return;
Sean Condon334ad692016-12-13 17:56:56 +0000182 }
183
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300184 netconfReplyTimeout = newNetconfReplyTimeout;
Sean Condon334ad692016-12-13 17:56:56 +0000185 netconfConnectTimeout = newNetconfConnectTimeout;
Sean Condon7347de92017-07-21 12:17:25 +0100186 netconfIdleTimeout = newNetconfIdleTimeout;
Andrea Campanella7bbe7b12017-05-03 16:03:38 -0700187 sshLibrary = newSshLibrary;
188 log.info("Settings: {} = {}, {} = {}, {} = {}",
189 PROP_NETCONF_REPLY_TIMEOUT, netconfReplyTimeout,
190 PROP_NETCONF_CONNECT_TIMEOUT, netconfConnectTimeout,
Sean Condon7347de92017-07-21 12:17:25 +0100191 PROP_NETCONF_IDLE_TIMEOUT, netconfIdleTimeout,
Andrea Campanella7bbe7b12017-05-03 16:03:38 -0700192 SSH_LIBRARY, sshLibrary);
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300193 }
194
andreaeb70a942015-10-16 21:34:46 -0700195 @Override
196 public void addDeviceListener(NetconfDeviceListener listener) {
197 if (!netconfDeviceListeners.contains(listener)) {
198 netconfDeviceListeners.add(listener);
199 }
200 }
201
202 @Override
203 public void removeDeviceListener(NetconfDeviceListener listener) {
204 netconfDeviceListeners.remove(listener);
205 }
206
207 @Override
208 public NetconfDevice getNetconfDevice(DeviceId deviceInfo) {
209 return netconfDeviceMap.get(deviceInfo);
210 }
211
212 @Override
213 public NetconfDevice getNetconfDevice(IpAddress ip, int port) {
andreaeb70a942015-10-16 21:34:46 -0700214 for (DeviceId info : netconfDeviceMap.keySet()) {
Andrea Campanella57efbb22016-02-11 14:21:41 -0800215 if (info.uri().getSchemeSpecificPart().equals(ip.toString() + ":" + port)) {
andreaeb70a942015-10-16 21:34:46 -0700216 return netconfDeviceMap.get(info);
217 }
218 }
Andrea Campanella1cd641b2015-12-07 17:28:34 -0800219 return null;
andreaeb70a942015-10-16 21:34:46 -0700220 }
221
222 @Override
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700223 public NetconfDevice connectDevice(DeviceId deviceId) throws NetconfException {
224 if (netconfDeviceMap.containsKey(deviceId)) {
225 log.debug("Device {} is already present", deviceId);
226 return netconfDeviceMap.get(deviceId);
andreaeb70a942015-10-16 21:34:46 -0700227 } else {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700228 log.debug("Creating NETCONF device {}", deviceId);
229 Device device = deviceService.getDevice(deviceId);
230 String ip;
231 int port;
232 if (device != null) {
233 ip = device.annotations().value("ipaddress");
234 port = Integer.parseInt(device.annotations().value("port"));
235 } else {
236 String[] info = deviceId.toString().split(":");
237 if (info.length == 3) {
238 ip = info[1];
239 port = Integer.parseInt(info[2]);
240 } else {
241 ip = Arrays.asList(info).stream().filter(el -> !el.equals(info[0])
Andrea Campanellac3627842017-04-04 18:06:54 +0200242 && !el.equals(info[info.length - 1]))
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700243 .reduce((t, u) -> t + ":" + u)
244 .get();
245 log.debug("ip v6 {}", ip);
246 port = Integer.parseInt(info[info.length - 1]);
247 }
248 }
249 try {
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600250 DeviceKey deviceKey = deviceKeyService.getDeviceKey(
Andrea Campanellac3627842017-04-04 18:06:54 +0200251 DeviceKeyId.deviceKeyId(deviceId.toString()));
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600252 NetconfDeviceInfo deviceInfo = null;
253 if (deviceKey.type() == DeviceKey.Type.USERNAME_PASSWORD) {
254 UsernamePassword usernamepasswd = deviceKey.asUsernamePassword();
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700255
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600256 deviceInfo = new NetconfDeviceInfo(usernamepasswd.username(),
Andrea Campanellac3627842017-04-04 18:06:54 +0200257 usernamepasswd.password(),
258 IpAddress.valueOf(ip),
259 port);
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600260
261 } else if (deviceKey.type() == DeviceKey.Type.SSL_KEY) {
262 String username = deviceKey.annotations().value(AnnotationKeys.USERNAME);
263 String password = deviceKey.annotations().value(AnnotationKeys.PASSWORD);
Andrea Campanellac3627842017-04-04 18:06:54 +0200264 String sshkey = deviceKey.annotations().value(AnnotationKeys.SSHKEY);
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600265
266 deviceInfo = new NetconfDeviceInfo(username,
Andrea Campanellac3627842017-04-04 18:06:54 +0200267 password,
268 IpAddress.valueOf(ip),
269 port,
270 sshkey);
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600271 } else {
272 log.error("Unknown device key for device {}", deviceId);
273 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700274 NetconfDevice netconfDevicedevice = createDevice(deviceInfo);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700275 netconfDevicedevice.getSession().addDeviceOutputListener(downListener);
276 return netconfDevicedevice;
277 } catch (NullPointerException e) {
278 throw new NetconfException("No Device Key for device " + deviceId, e);
279 }
andreaeb70a942015-10-16 21:34:46 -0700280 }
281 }
282
283 @Override
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700284 public void disconnectDevice(DeviceId deviceId, boolean remove) {
285 if (!netconfDeviceMap.containsKey(deviceId)) {
286 log.warn("Device {} is not present", deviceId);
andreaeb70a942015-10-16 21:34:46 -0700287 } else {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700288 stopDevice(deviceId, remove);
andreaeb70a942015-10-16 21:34:46 -0700289 }
290 }
291
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700292 private void stopDevice(DeviceId deviceId, boolean remove) {
293 netconfDeviceMap.get(deviceId).disconnect();
294 netconfDeviceMap.remove(deviceId);
295 if (remove) {
Andrea Campanella86294db2016-03-07 11:42:49 -0800296 for (NetconfDeviceListener l : netconfDeviceListeners) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700297 l.deviceRemoved(deviceId);
298 }
299 }
300 }
301
302 @Override
303 public void removeDevice(DeviceId deviceId) {
304 if (!netconfDeviceMap.containsKey(deviceId)) {
305 log.warn("Device {} is not present", deviceId);
306 for (NetconfDeviceListener l : netconfDeviceListeners) {
307 l.deviceRemoved(deviceId);
308 }
309 } else {
310 netconfDeviceMap.remove(deviceId);
311 for (NetconfDeviceListener l : netconfDeviceListeners) {
312 l.deviceRemoved(deviceId);
Andrea Campanella86294db2016-03-07 11:42:49 -0800313 }
314 }
315 }
316
Andrea Campanella101417d2015-12-11 17:58:07 -0800317 private NetconfDevice createDevice(NetconfDeviceInfo deviceInfo) throws NetconfException {
Andrea Campanella950310c2016-02-12 18:14:38 -0800318 NetconfDevice netconfDevice = deviceFactory.createNetconfDevice(deviceInfo);
Andrea Campanella087ceb92015-12-07 09:58:34 -0800319 netconfDeviceMap.put(deviceInfo.getDeviceId(), netconfDevice);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700320 for (NetconfDeviceListener l : netconfDeviceListeners) {
321 l.deviceAdded(deviceInfo.getDeviceId());
322 }
andreaeb70a942015-10-16 21:34:46 -0700323 return netconfDevice;
324 }
325
andreaeb70a942015-10-16 21:34:46 -0700326
327 @Override
328 public Map<DeviceId, NetconfDevice> getDevicesMap() {
329 return netconfDeviceMap;
330 }
Andrea Campanella950310c2016-02-12 18:14:38 -0800331
Andrea Campanella86294db2016-03-07 11:42:49 -0800332 @Override
333 public Set<DeviceId> getNetconfDevices() {
334 return netconfDeviceMap.keySet();
335 }
336
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700337
Andrea Campanella950310c2016-02-12 18:14:38 -0800338 //Device factory for the specific NetconfDeviceImpl
339 private class DefaultNetconfDeviceFactory implements NetconfDeviceFactory {
340
341 @Override
Andrea Campanella7bbe7b12017-05-03 16:03:38 -0700342 public NetconfDevice createNetconfDevice(NetconfDeviceInfo netconfDeviceInfo)
343 throws NetconfException {
344 if (sshLibrary.equals(ETHZ_SSH2)) {
345 return new DefaultNetconfDevice(netconfDeviceInfo,
346 new NetconfSessionImpl.SshNetconfSessionFactory());
347 }
Andrea Campanella950310c2016-02-12 18:14:38 -0800348 return new DefaultNetconfDevice(netconfDeviceInfo);
349 }
350 }
Andrea Campanella2464dc32016-02-17 17:54:53 -0800351
Yuta HIGUCHIf7089102017-05-03 10:31:46 -0700352 //Listener for closed session with devices, gets triggered when devices goes down
353 // or sends the end pattern ]]>]]>
Andrea Campanella2464dc32016-02-17 17:54:53 -0800354 private class DeviceDownEventListener implements NetconfDeviceOutputEventListener {
355
356 @Override
357 public void event(NetconfDeviceOutputEvent event) {
Andrea Campanellac3627842017-04-04 18:06:54 +0200358 DeviceId did = event.getDeviceInfo().getDeviceId();
Andrea Campanella2464dc32016-02-17 17:54:53 -0800359 if (event.type().equals(NetconfDeviceOutputEvent.Type.DEVICE_UNREGISTERED)) {
Andrea Campanellac3627842017-04-04 18:06:54 +0200360 removeDevice(did);
361 } else if (event.type().equals(NetconfDeviceOutputEvent.Type.SESSION_CLOSED)) {
362 log.info("Trying to reestablish connection with device {}", did);
363 executor.execute(() -> {
364 try {
Andrea Campanella7bbe7b12017-05-03 16:03:38 -0700365 NetconfDevice device = netconfDeviceMap.get(did);
366 if (device != null) {
367 device.getSession().checkAndReestablish();
368 log.info("Connection with device {} was reestablished", did);
369 } else {
370 log.warn("The device {} is not in the system", did);
371 }
372
Andrea Campanellac3627842017-04-04 18:06:54 +0200373 } catch (NetconfException e) {
374 log.error("The SSH connection with device {} couldn't be " +
375 "reestablished due to {}. " +
376 "Marking the device as unreachable", e.getMessage());
377 log.debug("Complete exception: ", e);
378 removeDevice(did);
379 }
380 });
Andrea Campanella2464dc32016-02-17 17:54:53 -0800381 }
382 }
383
384 @Override
385 public boolean isRelevant(NetconfDeviceOutputEvent event) {
386 return getDevicesMap().containsKey(event.getDeviceInfo().getDeviceId());
387 }
388 }
andreaeb70a942015-10-16 21:34:46 -0700389}