blob: 59ed7d4ab7ab1a9361399be75c608be7ddcecdc0 [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
17package org.onosproject.netconf.ctl;
18
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 {
Sean Condon334ad692016-12-13 17:56:56 +000068 private static final int DEFAULT_CONNECT_TIMEOUT_SECONDS = 5;
69 private static final String PROP_NETCONF_CONNECT_TIMEOUT = "netconfConnectTimeout";
70 @Property(name = PROP_NETCONF_CONNECT_TIMEOUT, intValue = DEFAULT_CONNECT_TIMEOUT_SECONDS,
71 label = "Time (in seconds) to wait for a NETCONF connect.")
72 protected static int netconfConnectTimeout = DEFAULT_CONNECT_TIMEOUT_SECONDS;
73
Andreas Papazois4752cfa2016-04-25 14:52:12 +030074 private static final String PROP_NETCONF_REPLY_TIMEOUT = "netconfReplyTimeout";
75 private static final int DEFAULT_REPLY_TIMEOUT_SECONDS = 5;
76 @Property(name = PROP_NETCONF_REPLY_TIMEOUT, intValue = DEFAULT_REPLY_TIMEOUT_SECONDS,
77 label = "Time (in seconds) waiting for a NetConf reply")
78 protected static int netconfReplyTimeout = DEFAULT_REPLY_TIMEOUT_SECONDS;
79
80 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
81 protected ComponentConfigService cfgService;
82
Andrea Campanella7e6200a2016-03-21 09:48:40 -070083 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
84 protected DeviceService deviceService;
85
86 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
87 protected DeviceKeyService deviceKeyService;
andreaeb70a942015-10-16 21:34:46 -070088
89 public static final Logger log = LoggerFactory
90 .getLogger(NetconfControllerImpl.class);
91
Andrea Campanella8b1cb672016-01-25 13:58:58 -080092 private Map<DeviceId, NetconfDevice> netconfDeviceMap = new ConcurrentHashMap<>();
andreaeb70a942015-10-16 21:34:46 -070093
Andrea Campanella2464dc32016-02-17 17:54:53 -080094 private final NetconfDeviceOutputEventListener downListener = new DeviceDownEventListener();
95
andreaeb70a942015-10-16 21:34:46 -070096 protected Set<NetconfDeviceListener> netconfDeviceListeners = new CopyOnWriteArraySet<>();
Andrea Campanella950310c2016-02-12 18:14:38 -080097 protected NetconfDeviceFactory deviceFactory = new DefaultNetconfDeviceFactory();
andreaeb70a942015-10-16 21:34:46 -070098
Andrea Campanellac3627842017-04-04 18:06:54 +020099 protected final ExecutorService executor =
100 Executors.newCachedThreadPool(groupedThreads("onos/netconfdevicecontroller",
101 "connection-reopen-%d", log));
102
andreaeb70a942015-10-16 21:34:46 -0700103 @Activate
104 public void activate(ComponentContext context) {
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300105 cfgService.registerProperties(getClass());
106 modified(context);
andreaeb70a942015-10-16 21:34:46 -0700107 log.info("Started");
108 }
109
110 @Deactivate
111 public void deactivate() {
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300112 cfgService.unregisterProperties(getClass(), false);
andreaeb70a942015-10-16 21:34:46 -0700113 netconfDeviceMap.clear();
114 log.info("Stopped");
115 }
116
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300117 @Modified
118 public void modified(ComponentContext context) {
119 if (context == null) {
120 netconfReplyTimeout = DEFAULT_REPLY_TIMEOUT_SECONDS;
Sean Condon334ad692016-12-13 17:56:56 +0000121 netconfConnectTimeout = DEFAULT_CONNECT_TIMEOUT_SECONDS;
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300122 log.info("No component configuration");
123 return;
124 }
125
126 Dictionary<?, ?> properties = context.getProperties();
127
128 int newNetconfReplyTimeout;
Sean Condon334ad692016-12-13 17:56:56 +0000129 int newNetconfConnectTimeout;
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300130 try {
131 String s = get(properties, PROP_NETCONF_REPLY_TIMEOUT);
132 newNetconfReplyTimeout = isNullOrEmpty(s) ?
133 netconfReplyTimeout : Integer.parseInt(s.trim());
Sean Condon334ad692016-12-13 17:56:56 +0000134
135 s = get(properties, PROP_NETCONF_CONNECT_TIMEOUT);
136 newNetconfConnectTimeout = isNullOrEmpty(s) ?
137 netconfConnectTimeout : Integer.parseInt(s.trim());
138
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300139 } catch (NumberFormatException e) {
140 log.warn("Component configuration had invalid value", e);
141 return;
142 }
143
Sean Condon334ad692016-12-13 17:56:56 +0000144 if (newNetconfConnectTimeout < 0) {
145 log.warn("netconfConnectTimeout is invalid - less than 0");
146 return;
147 } else if (newNetconfReplyTimeout <= 0) {
148 log.warn("netconfReplyTimeout is invalid - 0 or less.");
149 return;
150 }
151
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300152 netconfReplyTimeout = newNetconfReplyTimeout;
Sean Condon334ad692016-12-13 17:56:56 +0000153 netconfConnectTimeout = newNetconfConnectTimeout;
154 log.info("Settings: {} = {}, {} = {}",
Andrea Campanellac3627842017-04-04 18:06:54 +0200155 PROP_NETCONF_REPLY_TIMEOUT, netconfReplyTimeout, PROP_NETCONF_CONNECT_TIMEOUT, netconfConnectTimeout);
Andreas Papazois4752cfa2016-04-25 14:52:12 +0300156 }
157
andreaeb70a942015-10-16 21:34:46 -0700158 @Override
159 public void addDeviceListener(NetconfDeviceListener listener) {
160 if (!netconfDeviceListeners.contains(listener)) {
161 netconfDeviceListeners.add(listener);
162 }
163 }
164
165 @Override
166 public void removeDeviceListener(NetconfDeviceListener listener) {
167 netconfDeviceListeners.remove(listener);
168 }
169
170 @Override
171 public NetconfDevice getNetconfDevice(DeviceId deviceInfo) {
172 return netconfDeviceMap.get(deviceInfo);
173 }
174
175 @Override
176 public NetconfDevice getNetconfDevice(IpAddress ip, int port) {
andreaeb70a942015-10-16 21:34:46 -0700177 for (DeviceId info : netconfDeviceMap.keySet()) {
Andrea Campanella57efbb22016-02-11 14:21:41 -0800178 if (info.uri().getSchemeSpecificPart().equals(ip.toString() + ":" + port)) {
andreaeb70a942015-10-16 21:34:46 -0700179 return netconfDeviceMap.get(info);
180 }
181 }
Andrea Campanella1cd641b2015-12-07 17:28:34 -0800182 return null;
andreaeb70a942015-10-16 21:34:46 -0700183 }
184
185 @Override
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700186 public NetconfDevice connectDevice(DeviceId deviceId) throws NetconfException {
187 if (netconfDeviceMap.containsKey(deviceId)) {
188 log.debug("Device {} is already present", deviceId);
189 return netconfDeviceMap.get(deviceId);
andreaeb70a942015-10-16 21:34:46 -0700190 } else {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700191 log.debug("Creating NETCONF device {}", deviceId);
192 Device device = deviceService.getDevice(deviceId);
193 String ip;
194 int port;
195 if (device != null) {
196 ip = device.annotations().value("ipaddress");
197 port = Integer.parseInt(device.annotations().value("port"));
198 } else {
199 String[] info = deviceId.toString().split(":");
200 if (info.length == 3) {
201 ip = info[1];
202 port = Integer.parseInt(info[2]);
203 } else {
204 ip = Arrays.asList(info).stream().filter(el -> !el.equals(info[0])
Andrea Campanellac3627842017-04-04 18:06:54 +0200205 && !el.equals(info[info.length - 1]))
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700206 .reduce((t, u) -> t + ":" + u)
207 .get();
208 log.debug("ip v6 {}", ip);
209 port = Integer.parseInt(info[info.length - 1]);
210 }
211 }
212 try {
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600213 DeviceKey deviceKey = deviceKeyService.getDeviceKey(
Andrea Campanellac3627842017-04-04 18:06:54 +0200214 DeviceKeyId.deviceKeyId(deviceId.toString()));
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600215 NetconfDeviceInfo deviceInfo = null;
216 if (deviceKey.type() == DeviceKey.Type.USERNAME_PASSWORD) {
217 UsernamePassword usernamepasswd = deviceKey.asUsernamePassword();
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700218
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600219 deviceInfo = new NetconfDeviceInfo(usernamepasswd.username(),
Andrea Campanellac3627842017-04-04 18:06:54 +0200220 usernamepasswd.password(),
221 IpAddress.valueOf(ip),
222 port);
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600223
224 } else if (deviceKey.type() == DeviceKey.Type.SSL_KEY) {
225 String username = deviceKey.annotations().value(AnnotationKeys.USERNAME);
226 String password = deviceKey.annotations().value(AnnotationKeys.PASSWORD);
Andrea Campanellac3627842017-04-04 18:06:54 +0200227 String sshkey = deviceKey.annotations().value(AnnotationKeys.SSHKEY);
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600228
229 deviceInfo = new NetconfDeviceInfo(username,
Andrea Campanellac3627842017-04-04 18:06:54 +0200230 password,
231 IpAddress.valueOf(ip),
232 port,
233 sshkey);
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600234 } else {
235 log.error("Unknown device key for device {}", deviceId);
236 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700237 NetconfDevice netconfDevicedevice = createDevice(deviceInfo);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700238 netconfDevicedevice.getSession().addDeviceOutputListener(downListener);
239 return netconfDevicedevice;
240 } catch (NullPointerException e) {
241 throw new NetconfException("No Device Key for device " + deviceId, e);
242 }
andreaeb70a942015-10-16 21:34:46 -0700243 }
244 }
245
246 @Override
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700247 public void disconnectDevice(DeviceId deviceId, boolean remove) {
248 if (!netconfDeviceMap.containsKey(deviceId)) {
249 log.warn("Device {} is not present", deviceId);
andreaeb70a942015-10-16 21:34:46 -0700250 } else {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700251 stopDevice(deviceId, remove);
andreaeb70a942015-10-16 21:34:46 -0700252 }
253 }
254
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700255 private void stopDevice(DeviceId deviceId, boolean remove) {
256 netconfDeviceMap.get(deviceId).disconnect();
257 netconfDeviceMap.remove(deviceId);
258 if (remove) {
Andrea Campanella86294db2016-03-07 11:42:49 -0800259 for (NetconfDeviceListener l : netconfDeviceListeners) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700260 l.deviceRemoved(deviceId);
261 }
262 }
263 }
264
265 @Override
266 public void removeDevice(DeviceId deviceId) {
267 if (!netconfDeviceMap.containsKey(deviceId)) {
268 log.warn("Device {} is not present", deviceId);
269 for (NetconfDeviceListener l : netconfDeviceListeners) {
270 l.deviceRemoved(deviceId);
271 }
272 } else {
273 netconfDeviceMap.remove(deviceId);
274 for (NetconfDeviceListener l : netconfDeviceListeners) {
275 l.deviceRemoved(deviceId);
Andrea Campanella86294db2016-03-07 11:42:49 -0800276 }
277 }
278 }
279
Andrea Campanella101417d2015-12-11 17:58:07 -0800280 private NetconfDevice createDevice(NetconfDeviceInfo deviceInfo) throws NetconfException {
Andrea Campanella950310c2016-02-12 18:14:38 -0800281 NetconfDevice netconfDevice = deviceFactory.createNetconfDevice(deviceInfo);
Andrea Campanella087ceb92015-12-07 09:58:34 -0800282 netconfDeviceMap.put(deviceInfo.getDeviceId(), netconfDevice);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700283 for (NetconfDeviceListener l : netconfDeviceListeners) {
284 l.deviceAdded(deviceInfo.getDeviceId());
285 }
andreaeb70a942015-10-16 21:34:46 -0700286 return netconfDevice;
287 }
288
andreaeb70a942015-10-16 21:34:46 -0700289
290 @Override
291 public Map<DeviceId, NetconfDevice> getDevicesMap() {
292 return netconfDeviceMap;
293 }
Andrea Campanella950310c2016-02-12 18:14:38 -0800294
Andrea Campanella86294db2016-03-07 11:42:49 -0800295 @Override
296 public Set<DeviceId> getNetconfDevices() {
297 return netconfDeviceMap.keySet();
298 }
299
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700300
Andrea Campanella950310c2016-02-12 18:14:38 -0800301 //Device factory for the specific NetconfDeviceImpl
302 private class DefaultNetconfDeviceFactory implements NetconfDeviceFactory {
303
304 @Override
305 public NetconfDevice createNetconfDevice(NetconfDeviceInfo netconfDeviceInfo) throws NetconfException {
306 return new DefaultNetconfDevice(netconfDeviceInfo);
307 }
308 }
Andrea Campanella2464dc32016-02-17 17:54:53 -0800309
310 //Listener for closed session with devices, gets triggered whe devices goes down
311 // or sends the endpattern ]]>]]>
312 private class DeviceDownEventListener implements NetconfDeviceOutputEventListener {
313
314 @Override
315 public void event(NetconfDeviceOutputEvent event) {
Andrea Campanellac3627842017-04-04 18:06:54 +0200316 DeviceId did = event.getDeviceInfo().getDeviceId();
Andrea Campanella2464dc32016-02-17 17:54:53 -0800317 if (event.type().equals(NetconfDeviceOutputEvent.Type.DEVICE_UNREGISTERED)) {
Andrea Campanellac3627842017-04-04 18:06:54 +0200318 removeDevice(did);
319 } else if (event.type().equals(NetconfDeviceOutputEvent.Type.SESSION_CLOSED)) {
320 log.info("Trying to reestablish connection with device {}", did);
321 executor.execute(() -> {
322 try {
323 netconfDeviceMap.get(did).getSession().checkAndReestablish();
324 log.info("Connection with device {} was reestablished", did);
325 } catch (NetconfException e) {
326 log.error("The SSH connection with device {} couldn't be " +
327 "reestablished due to {}. " +
328 "Marking the device as unreachable", e.getMessage());
329 log.debug("Complete exception: ", e);
330 removeDevice(did);
331 }
332 });
Andrea Campanella2464dc32016-02-17 17:54:53 -0800333 }
334 }
335
336 @Override
337 public boolean isRelevant(NetconfDeviceOutputEvent event) {
338 return getDevicesMap().containsKey(event.getDeviceInfo().getDeviceId());
339 }
340 }
andreaeb70a942015-10-16 21:34:46 -0700341}