blob: 86b27f63a532d18a832b146c9fcd2bb7ca3be4be [file] [log] [blame]
Sanjay Se8dcfee2015-04-23 10:07:08 +05301/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Sanjay Se8dcfee2015-04-23 10:07:08 +05303 *
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 */
andreaeb70a942015-10-16 21:34:46 -070016
Sanjay Se8dcfee2015-04-23 10:07:08 +053017package org.onosproject.provider.netconf.device.impl;
18
Andrea Campanella34cf65c2017-04-12 13:51:32 +020019import com.fasterxml.jackson.databind.ObjectMapper;
20import com.fasterxml.jackson.databind.node.ObjectNode;
Michele Santuari00cc1f72016-09-08 17:05:24 +020021import com.google.common.base.Objects;
andreaeb70a942015-10-16 21:34:46 -070022import com.google.common.base.Preconditions;
Andrea Campanella34cf65c2017-04-12 13:51:32 +020023import com.google.common.collect.ImmutableList;
Sanjay Se8dcfee2015-04-23 10:07:08 +053024import org.apache.felix.scr.annotations.Activate;
25import org.apache.felix.scr.annotations.Component;
26import org.apache.felix.scr.annotations.Deactivate;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -070027import org.apache.felix.scr.annotations.Modified;
28import org.apache.felix.scr.annotations.Property;
Sanjay Se8dcfee2015-04-23 10:07:08 +053029import org.apache.felix.scr.annotations.Reference;
30import org.apache.felix.scr.annotations.ReferenceCardinality;
31import org.onlab.packet.ChassisId;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -070032import org.onlab.util.Tools;
33import org.onosproject.cfg.ComponentConfigService;
andreaeb70a942015-10-16 21:34:46 -070034import org.onosproject.core.ApplicationId;
35import org.onosproject.core.CoreService;
Andrea Campanella32813682017-10-23 15:29:24 +020036import org.onosproject.net.Port;
37import org.onosproject.net.behaviour.PortAdmin;
Ray Milkey6c013742017-08-15 10:16:43 -070038import org.onosproject.net.config.ConfigException;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070039import org.onosproject.mastership.MastershipService;
Marc De Leenheerb0d131c2016-03-01 20:34:59 -080040import org.onosproject.net.AnnotationKeys;
andreaeb70a942015-10-16 21:34:46 -070041import org.onosproject.net.DefaultAnnotations;
Sanjay Se8dcfee2015-04-23 10:07:08 +053042import org.onosproject.net.Device;
43import org.onosproject.net.DeviceId;
44import org.onosproject.net.MastershipRole;
Saurav Dasa2d37502016-03-25 17:50:40 -070045import org.onosproject.net.PortNumber;
andreaeb70a942015-10-16 21:34:46 -070046import org.onosproject.net.SparseAnnotations;
47import org.onosproject.net.config.ConfigFactory;
48import org.onosproject.net.config.NetworkConfigEvent;
49import org.onosproject.net.config.NetworkConfigListener;
50import org.onosproject.net.config.NetworkConfigRegistry;
Andrea Campanella34cf65c2017-04-12 13:51:32 +020051import org.onosproject.net.config.basics.SubjectFactories;
Sanjay Se8dcfee2015-04-23 10:07:08 +053052import org.onosproject.net.device.DefaultDeviceDescription;
Andrea Campanella32813682017-10-23 15:29:24 +020053import org.onosproject.net.device.DefaultPortDescription;
Sanjay Se8dcfee2015-04-23 10:07:08 +053054import org.onosproject.net.device.DeviceDescription;
Andrea Campanella6c71a052016-04-22 11:56:31 -070055import org.onosproject.net.device.DeviceDescriptionDiscovery;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070056import org.onosproject.net.device.DeviceEvent;
57import org.onosproject.net.device.DeviceListener;
Sanjay Se8dcfee2015-04-23 10:07:08 +053058import org.onosproject.net.device.DeviceProvider;
59import org.onosproject.net.device.DeviceProviderRegistry;
60import org.onosproject.net.device.DeviceProviderService;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -080061import org.onosproject.net.device.DeviceService;
Andrea Campanellac3627842017-04-04 18:06:54 +020062import org.onosproject.net.device.PortStatistics;
Gaurav Agrawaldab4d772017-03-29 15:15:13 +053063import org.onosproject.net.device.PortStatisticsDiscovery;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070064import org.onosproject.net.key.DeviceKey;
65import org.onosproject.net.key.DeviceKeyAdminService;
66import org.onosproject.net.key.DeviceKeyId;
Sanjay Se8dcfee2015-04-23 10:07:08 +053067import org.onosproject.net.provider.AbstractProvider;
68import org.onosproject.net.provider.ProviderId;
andreaeb70a942015-10-16 21:34:46 -070069import org.onosproject.netconf.NetconfController;
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -070070import org.onosproject.netconf.NetconfDevice;
andreaeb70a942015-10-16 21:34:46 -070071import org.onosproject.netconf.NetconfDeviceListener;
Andrea Campanella8b1cb672016-01-25 13:58:58 -080072import org.onosproject.netconf.NetconfException;
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -070073import org.onosproject.netconf.config.NetconfDeviceConfig;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -070074import org.osgi.service.component.ComponentContext;
Sanjay Se8dcfee2015-04-23 10:07:08 +053075import org.slf4j.Logger;
76
Andrea Campanella087ceb92015-12-07 09:58:34 -080077import java.io.IOException;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070078import java.net.Socket;
79import java.net.URI;
80import java.net.URISyntaxException;
81import java.util.Arrays;
Andrea Campanellac3627842017-04-04 18:06:54 +020082import java.util.Collection;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -070083import java.util.Dictionary;
Andrea Campanella34cf65c2017-04-12 13:51:32 +020084import java.util.List;
mskala832d0472017-06-09 16:31:42 +020085import java.util.Map;
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -070086import java.util.Optional;
Andrea Campanella34cf65c2017-04-12 13:51:32 +020087import java.util.Set;
Andrea Campanella32813682017-10-23 15:29:24 +020088import java.util.concurrent.CompletableFuture;
mskala832d0472017-06-09 16:31:42 +020089import java.util.concurrent.ConcurrentHashMap;
Andrea Campanella5c999e22016-03-01 15:12:53 -080090import java.util.concurrent.ExecutorService;
91import java.util.concurrent.Executors;
helenyrwufd296b62016-06-22 17:43:02 -070092import java.util.concurrent.ScheduledExecutorService;
93import java.util.concurrent.ScheduledFuture;
94import java.util.concurrent.TimeUnit;
mskala832d0472017-06-09 16:31:42 +020095import java.util.concurrent.atomic.AtomicInteger;
andreaeb70a942015-10-16 21:34:46 -070096
Yuta HIGUCHI1624df12016-07-21 16:54:33 -070097import static java.util.concurrent.Executors.newScheduledThreadPool;
Andrea Campanella5c999e22016-03-01 15:12:53 -080098import static org.onlab.util.Tools.groupedThreads;
andreaeb70a942015-10-16 21:34:46 -070099import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
100import static org.slf4j.LoggerFactory.getLogger;
101
Sanjay Se8dcfee2015-04-23 10:07:08 +0530102/**
andreaeb70a942015-10-16 21:34:46 -0700103 * Provider which uses an NETCONF controller to detect device.
Sanjay Se8dcfee2015-04-23 10:07:08 +0530104 */
105@Component(immediate = true)
106public class NetconfDeviceProvider extends AbstractProvider
107 implements DeviceProvider {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700108
andreaeb70a942015-10-16 21:34:46 -0700109 private final Logger log = getLogger(getClass());
Sanjay Se8dcfee2015-04-23 10:07:08 +0530110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected DeviceProviderRegistry providerRegistry;
113
andreaeb70a942015-10-16 21:34:46 -0700114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella101417d2015-12-11 17:58:07 -0800115 protected NetconfController controller;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
andreaeb70a942015-10-16 21:34:46 -0700118 protected NetworkConfigRegistry cfgService;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530119
Thomas Vachuskad6811712015-04-29 21:37:04 -0700120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
andreaeb70a942015-10-16 21:34:46 -0700121 protected CoreService coreService;
Thomas Vachuskad6811712015-04-29 21:37:04 -0700122
Aaron Kruglikov17b4c852016-01-15 16:37:04 -0800123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700124 protected DeviceService deviceService;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -0800125
126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700127 protected DeviceKeyAdminService deviceKeyAdminService;
128
129 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
130 protected MastershipService mastershipService;
131
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700132 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
133 protected ComponentConfigService componentConfigService;
134
135
Michele Santuari576f09c2016-09-28 14:20:00 +0200136 protected static final String APP_NAME = "org.onosproject.netconf";
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200137 protected static final String SCHEME_NAME = "netconf";
Andrea Campanella101417d2015-12-11 17:58:07 -0800138 private static final String DEVICE_PROVIDER_PACKAGE = "org.onosproject.netconf.provider.device";
139 private static final String UNKNOWN = "unknown";
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700140 protected static final String ISNULL = "NetconfDeviceInfo is null";
141 private static final String IPADDRESS = "ipaddress";
142 private static final String NETCONF = "netconf";
143 private static final String PORT = "port";
helenyrwufd296b62016-06-22 17:43:02 -0700144 private static final int CORE_POOL_SIZE = 10;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700145
Thomas Vachuskadb29dcf2017-03-31 11:26:19 -0700146 private static final int DEFAULT_POLL_FREQUENCY_SECONDS = 30;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700147 @Property(name = "pollFrequency", intValue = DEFAULT_POLL_FREQUENCY_SECONDS,
148 label = "Configure poll frequency for port status and statistics; " +
Thomas Vachuskadb29dcf2017-03-31 11:26:19 -0700149 "default is 30 sec")
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700150 private int pollFrequency = DEFAULT_POLL_FREQUENCY_SECONDS;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530151
mskala832d0472017-06-09 16:31:42 +0200152 private static final int DEFAULT_MAX_RETRIES = 5;
153 @Property(name = "maxRetries", intValue = DEFAULT_MAX_RETRIES,
154 label = "Configure maximum allowed number of retries for obtaining list of ports; " +
155 "default is 5 times")
156 private int maxRetries = DEFAULT_MAX_RETRIES;
157
Michal Mach26a90fa2017-06-07 11:12:46 +0200158 protected ExecutorService executor =
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700159 Executors.newFixedThreadPool(5, groupedThreads("onos/netconfdeviceprovider",
160 "device-installer-%d", log));
Yuta HIGUCHI1624df12016-07-21 16:54:33 -0700161 protected ScheduledExecutorService connectionExecutor
162 = newScheduledThreadPool(CORE_POOL_SIZE,
163 groupedThreads("onos/netconfdeviceprovider",
164 "connection-executor-%d", log));
Andrea Campanella5c999e22016-03-01 15:12:53 -0800165
Michele Santuari576f09c2016-09-28 14:20:00 +0200166 protected DeviceProviderService providerService;
andreaeb70a942015-10-16 21:34:46 -0700167 private NetconfDeviceListener innerNodeListener = new InnerNetconfDeviceListener();
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700168 private InternalDeviceListener deviceListener = new InternalDeviceListener();
mskala832d0472017-06-09 16:31:42 +0200169 private final Map<DeviceId, AtomicInteger> retriedPortDiscoveryMap = new ConcurrentHashMap<>();
Michele Santuari576f09c2016-09-28 14:20:00 +0200170 protected ScheduledFuture<?> scheduledTask;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530171
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200172 protected final List<ConfigFactory> factories = ImmutableList.of(
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700173 // TODO consider moving Config registration to NETCONF ctl bundle
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200174 new ConfigFactory<DeviceId, NetconfDeviceConfig>(
175 SubjectFactories.DEVICE_SUBJECT_FACTORY,
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700176 NetconfDeviceConfig.class, NetconfDeviceConfig.CONFIG_KEY) {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200177 @Override
178 public NetconfDeviceConfig createConfig() {
179 return new NetconfDeviceConfig();
180 }
181 },
andreaeb70a942015-10-16 21:34:46 -0700182 new ConfigFactory<ApplicationId, NetconfProviderConfig>(APP_SUBJECT_FACTORY,
183 NetconfProviderConfig.class,
Palash Kalac3ffad92017-06-09 21:18:19 +0900184 "netconf_devices",
andreaeb70a942015-10-16 21:34:46 -0700185 true) {
186 @Override
187 public NetconfProviderConfig createConfig() {
188 return new NetconfProviderConfig();
189 }
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200190 });
191
Michele Santuari576f09c2016-09-28 14:20:00 +0200192 protected final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
andreaeb70a942015-10-16 21:34:46 -0700193 private ApplicationId appId;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700194 private boolean active;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530195
Sanjay Se8dcfee2015-04-23 10:07:08 +0530196
197 @Activate
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700198 public void activate(ComponentContext context) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700199 active = true;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700200 componentConfigService.registerProperties(getClass());
Sanjay Se8dcfee2015-04-23 10:07:08 +0530201 providerService = providerRegistry.register(this);
Andrea Campanella101417d2015-12-11 17:58:07 -0800202 appId = coreService.registerApplication(APP_NAME);
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200203 factories.forEach(cfgService::registerConfigFactory);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700204 cfgService.addListener(cfgListener);
andreaeb70a942015-10-16 21:34:46 -0700205 controller.addDeviceListener(innerNodeListener);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700206 deviceService.addListener(deviceListener);
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200207 translateConfig();
Andrea Campanella7d8449b2016-03-02 10:16:42 -0800208 executor.execute(NetconfDeviceProvider.this::connectDevices);
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700209 modified(context);
Thomas Vachuskad6811712015-04-29 21:37:04 -0700210 log.info("Started");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530211 }
212
andreaeb70a942015-10-16 21:34:46 -0700213
Sanjay Se8dcfee2015-04-23 10:07:08 +0530214 @Deactivate
andreaeb70a942015-10-16 21:34:46 -0700215 public void deactivate() {
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700216 componentConfigService.unregisterProperties(getClass(), false);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700217 deviceService.removeListener(deviceListener);
218 active = false;
219 controller.getNetconfDevices().forEach(id -> {
220 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(id.toString()));
221 controller.disconnectDevice(id, true);
222 });
Andrea Campanella86294db2016-03-07 11:42:49 -0800223 controller.removeDeviceListener(innerNodeListener);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700224 deviceService.removeListener(deviceListener);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530225 providerRegistry.unregister(this);
226 providerService = null;
mskala832d0472017-06-09 16:31:42 +0200227 retriedPortDiscoveryMap.clear();
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200228 factories.forEach(cfgService::unregisterConfigFactory);
helenyrwufd296b62016-06-22 17:43:02 -0700229 scheduledTask.cancel(true);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700230 executor.shutdown();
Sanjay Seb5eebb2015-04-24 15:44:50 +0530231 log.info("Stopped");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530232 }
233
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700234
235 @Modified
236 public void modified(ComponentContext context) {
237 if (context != null) {
238 Dictionary<?, ?> properties = context.getProperties();
239 pollFrequency = Tools.getIntegerProperty(properties, "pollFrequency",
240 DEFAULT_POLL_FREQUENCY_SECONDS);
241 log.info("Configured. Poll frequency is configured to {} seconds", pollFrequency);
mskala832d0472017-06-09 16:31:42 +0200242
243 maxRetries = Tools.getIntegerProperty(properties, "maxRetries",
244 DEFAULT_MAX_RETRIES);
245 log.info("Configured. Number of retries is configured to {} times", maxRetries);
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700246 }
247 if (scheduledTask != null) {
248 scheduledTask.cancel(false);
249 }
250 scheduledTask = schedulePolling();
251 }
252
andreaeb70a942015-10-16 21:34:46 -0700253 public NetconfDeviceProvider() {
Andrea Campanella101417d2015-12-11 17:58:07 -0800254 super(new ProviderId(SCHEME_NAME, DEVICE_PROVIDER_PACKAGE));
Sanjay Se8dcfee2015-04-23 10:07:08 +0530255 }
256
helenyrwufd296b62016-06-22 17:43:02 -0700257 // Checks connection to devices in the config file
258 // every DEFAULT_POLL_FREQUENCY_SECONDS seconds.
259 private ScheduledFuture schedulePolling() {
Yuta HIGUCHIc2b82e32017-03-10 14:33:41 -0800260 return connectionExecutor.scheduleAtFixedRate(exceptionSafe(this::checkAndUpdateDevices),
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700261 pollFrequency / 10,
262 pollFrequency, TimeUnit.SECONDS);
helenyrwufd296b62016-06-22 17:43:02 -0700263 }
264
Yuta HIGUCHIc2b82e32017-03-10 14:33:41 -0800265 private Runnable exceptionSafe(Runnable runnable) {
266 return new Runnable() {
267
268 @Override
269 public void run() {
270 try {
271 runnable.run();
272 } catch (Exception e) {
273 log.error("Unhandled Exception", e);
274 }
275 }
276 };
277 }
278
Sanjay Se8dcfee2015-04-23 10:07:08 +0530279 @Override
280 public void triggerProbe(DeviceId deviceId) {
andreaeb70a942015-10-16 21:34:46 -0700281 // TODO: This will be implemented later.
Yuta HIGUCHIe4cb8cf2017-05-01 22:18:28 -0700282 log.debug("Should be triggering probe on device {}", deviceId);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530283 }
284
285 @Override
286 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700287 if (active) {
288 switch (newRole) {
289 case MASTER:
290 initiateConnection(deviceId, newRole);
291 log.debug("Accepting mastership role change to {} for device {}", newRole, deviceId);
292 break;
293 case STANDBY:
294 controller.disconnectDevice(deviceId, false);
295 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.STANDBY);
296 //else no-op
297 break;
298 case NONE:
299 controller.disconnectDevice(deviceId, false);
300 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
301 break;
302 default:
303 log.error("Unimplemented Mastership state : {}", newRole);
304
305 }
306 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530307 }
308
309 @Override
310 public boolean isReachable(DeviceId deviceId) {
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700311
312 boolean sessionExists =
313 Optional.ofNullable(controller.getDevicesMap().get(deviceId))
314 .map(NetconfDevice::isActive)
315 .orElse(false);
316 if (sessionExists) {
317 return true;
318 }
319
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700320 //FIXME this is a workaround util device state is shared
321 // between controller instances.
322 Device device = deviceService.getDevice(deviceId);
323 String ip;
324 int port;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700325 if (device != null) {
326 ip = device.annotations().value(IPADDRESS);
327 port = Integer.parseInt(device.annotations().value(PORT));
328 } else {
329 String[] info = deviceId.toString().split(":");
330 if (info.length == 3) {
331 ip = info[1];
332 port = Integer.parseInt(info[2]);
333 } else {
334 ip = Arrays.asList(info).stream().filter(el -> !el.equals(info[0])
335 && !el.equals(info[info.length - 1]))
336 .reduce((t, u) -> t + ":" + u)
337 .get();
338 log.debug("ip v6 {}", ip);
339 port = Integer.parseInt(info[info.length - 1]);
340 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530341 }
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700342 // FIXME just opening TCP session probably is not the appropriate
343 // method to test reachability.
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700344 //test connection to device opening a socket to it.
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700345 log.debug("Testing reachability for {}:{}", ip, port);
Yuta HIGUCHI70c21472017-04-20 20:40:46 -0700346 try (Socket socket = new Socket(ip, port)) {
Yuta HIGUCHI0454d702017-03-17 10:08:38 -0700347 log.debug("rechability of {}, {}, {}", deviceId, socket.isConnected(), !socket.isClosed());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700348 return socket.isConnected() && !socket.isClosed();
349 } catch (IOException e) {
350 log.info("Device {} is not reachable", deviceId);
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700351 log.debug(" error details", e);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700352 return false;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700353 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530354 }
355
Saurav Dasa2d37502016-03-25 17:50:40 -0700356 @Override
357 public void changePortState(DeviceId deviceId, PortNumber portNumber,
358 boolean enable) {
Andrea Campanella32813682017-10-23 15:29:24 +0200359 Device device = deviceService.getDevice(deviceId);
360 if (mastershipService.isLocalMaster(deviceId)) {
361 if (device.is(PortAdmin.class)) {
362 PortAdmin portAdmin =
363 device.as(PortAdmin.class);
364 CompletableFuture<Boolean> modified;
365 if (enable) {
366 modified = portAdmin.enable(portNumber);
367 } else {
368 modified = portAdmin.disable(portNumber);
369 }
370 modified.thenAccept(result -> {
371 if (result) {
372 Port port = deviceService.getPort(deviceId, portNumber);
373 //rebuilding port description with admin state changed.
374 providerService.portStatusChanged(deviceId,
375 new DefaultPortDescription(portNumber, enable, false,
376 port.type(), port.portSpeed(),
377 (SparseAnnotations) port.annotations()));
378 } else {
379 log.warn("Your device {} port {} status can't be changed to {}",
380 deviceId, portNumber, enable);
381 }
382 });
383 } else {
384 log.warn("Device {} does not support Port Admin", deviceId);
385 }
386 } else {
387 log.debug("Not master but {}, not changing port state", mastershipService.getLocalRole(deviceId));
388 }
Saurav Dasa2d37502016-03-25 17:50:40 -0700389 }
390
andreaeb70a942015-10-16 21:34:46 -0700391 private class InnerNetconfDeviceListener implements NetconfDeviceListener {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530392
Andrea Campanella101417d2015-12-11 17:58:07 -0800393
andreaeb70a942015-10-16 21:34:46 -0700394 @Override
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700395 public void deviceAdded(DeviceId deviceId) {
396 //no-op
397 log.debug("Netconf device {} added to Netconf subController", deviceId);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530398 }
399
400 @Override
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700401 public void deviceRemoved(DeviceId deviceId) {
402 Preconditions.checkNotNull(deviceId, ISNULL);
helenyrwufd296b62016-06-22 17:43:02 -0700403
404 if (deviceService.getDevice(deviceId) != null) {
405 providerService.deviceDisconnected(deviceId);
mskala832d0472017-06-09 16:31:42 +0200406 retriedPortDiscoveryMap.remove(deviceId);
helenyrwufd296b62016-06-22 17:43:02 -0700407 log.debug("Netconf device {} removed from Netconf subController", deviceId);
408 } else {
409 log.warn("Netconf device {} does not exist in the store, " +
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530410 "it may already have been removed", deviceId);
helenyrwufd296b62016-06-22 17:43:02 -0700411 }
andreaeb70a942015-10-16 21:34:46 -0700412 }
413 }
414
andreaeb70a942015-10-16 21:34:46 -0700415 private void connectDevices() {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200416 Set<DeviceId> deviceSubjects =
417 cfgService.getSubjects(DeviceId.class, NetconfDeviceConfig.class);
418 deviceSubjects.forEach(deviceId -> {
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700419 connectDevice(cfgService.getConfig(deviceId, NetconfDeviceConfig.class));
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200420 });
helenyrwufd296b62016-06-22 17:43:02 -0700421 }
422
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700423
424 private void connectDevice(NetconfDeviceConfig config) {
425 if (config == null) {
426 return;
427 }
428 DeviceId deviceId = config.subject();
429 if (!deviceId.uri().getScheme().equals(SCHEME_NAME)) {
430 // not under my scheme, skipping
431 log.trace("{} not my scheme, skipping", deviceId);
432 return;
433 }
434 DeviceDescription deviceDescription = createDeviceRepresentation(deviceId, config);
435 log.debug("Connecting NETCONF device {}, on {}:{} with username {}",
436 deviceId, config.ip(), config.port(), config.username());
437 storeDeviceKey(config.sshKey(), config.username(), config.password(), deviceId);
Andrea Campanella105736e2017-11-23 12:52:43 +0100438 retriedPortDiscoveryMap.put(deviceId, new AtomicInteger(0));
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700439 if (deviceService.getDevice(deviceId) == null) {
440 providerService.deviceConnected(deviceId, deviceDescription);
441 }
442 try {
Andrea Campanella105736e2017-11-23 12:52:43 +0100443 checkAndUpdateDevice(deviceId, deviceDescription, true);
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700444 } catch (Exception e) {
445 log.error("Unhandled exception checking {}", deviceId, e);
446 }
447 }
448
Andrea Campanella105736e2017-11-23 12:52:43 +0100449 private void checkAndUpdateDevice(DeviceId deviceId, DeviceDescription deviceDescription, boolean newlyConnected) {
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530450 Device device = deviceService.getDevice(deviceId);
451 if (device == null) {
Andrea Campanella105736e2017-11-23 12:52:43 +0100452 log.debug("Device {} has not been added to store, since it's not reachable", deviceId);
453 return;
454 }
455 boolean isReachable = isReachable(deviceId);
456 if (!isReachable && deviceService.isAvailable(deviceId)) {
457 providerService.deviceDisconnected(deviceId);
458 return;
459 } else if (newlyConnected) {
460 updateDeviceDescription(deviceId, deviceDescription, device);
461 }
462 if (isReachable && deviceService.isAvailable(deviceId) &&
463 mastershipService.isLocalMaster(deviceId)) {
464 //if ports are not discovered, retry the discovery
465 if (deviceService.getPorts(deviceId).isEmpty() &&
466 retriedPortDiscoveryMap.get(deviceId).getAndIncrement() < maxRetries) {
467 discoverPorts(deviceId);
468 }
469 updatePortStatistics(device);
470 }
471 }
472
473 private void updateDeviceDescription(DeviceId deviceId, DeviceDescription deviceDescription, Device device) {
474 if (device.is(DeviceDescriptionDiscovery.class)) {
475 if (mastershipService.isLocalMaster(deviceId)) {
476 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
477 device.as(DeviceDescriptionDiscovery.class);
478 DeviceDescription updatedDeviceDescription =
479 deviceDescriptionDiscovery.discoverDeviceDetails();
480 if (updatedDeviceDescription != null &&
481 !descriptionEquals(device, updatedDeviceDescription)) {
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530482 providerService.deviceConnected(
483 deviceId, new DefaultDeviceDescription(
Andrea Campanella105736e2017-11-23 12:52:43 +0100484 updatedDeviceDescription, true,
485 updatedDeviceDescription.annotations()));
486 } else if (updatedDeviceDescription == null) {
487 providerService.deviceConnected(
488 deviceId, new DefaultDeviceDescription(
489 deviceDescription, true,
490 deviceDescription.annotations()));
Konstantinos Kanonakis4d67dd82016-08-05 12:18:52 -0500491 }
helenyrwufd296b62016-06-22 17:43:02 -0700492 }
Andrea Campanella105736e2017-11-23 12:52:43 +0100493 } else {
494 log.warn("No DeviceDescriptionDiscovery behaviour for device {} " +
495 "using DefaultDeviceDescription", deviceId);
496 providerService.deviceConnected(
497 deviceId, new DefaultDeviceDescription(
498 deviceDescription, true, deviceDescription.annotations()));
helenyrwufd296b62016-06-22 17:43:02 -0700499 }
500 }
501
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530502 private void updatePortStatistics(Device device) {
503 if (device.is(PortStatisticsDiscovery.class)) {
504 PortStatisticsDiscovery d = device.as(PortStatisticsDiscovery.class);
Andrea Campanellac3627842017-04-04 18:06:54 +0200505 Collection<PortStatistics> portStatistics = d.discoverPortStatistics();
506 if (portStatistics != null) {
507 providerService.updatePortStatistics(device.id(),
508 portStatistics);
509 }
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530510 } else {
Yuta HIGUCHIe4cb8cf2017-05-01 22:18:28 -0700511 log.debug("No port statistics getter behaviour for device {}",
512 device.id());
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530513 }
514 }
515
Michele Santuari00cc1f72016-09-08 17:05:24 +0200516 private boolean descriptionEquals(Device device, DeviceDescription updatedDeviceDescription) {
Yuta HIGUCHIf381fc72017-01-03 10:39:36 -0800517 return Objects.equal(device.id().uri(), updatedDeviceDescription.deviceUri())
Michele Santuari00cc1f72016-09-08 17:05:24 +0200518 && Objects.equal(device.type(), updatedDeviceDescription.type())
519 && Objects.equal(device.manufacturer(), updatedDeviceDescription.manufacturer())
520 && Objects.equal(device.hwVersion(), updatedDeviceDescription.hwVersion())
521 && Objects.equal(device.swVersion(), updatedDeviceDescription.swVersion())
522 && Objects.equal(device.serialNumber(), updatedDeviceDescription.serialNumber())
523 && Objects.equal(device.chassisId(), updatedDeviceDescription.chassisId())
524 && Objects.equal(device.annotations(), updatedDeviceDescription.annotations());
525 }
526
helenyrwufd296b62016-06-22 17:43:02 -0700527 private void checkAndUpdateDevices() {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200528 Set<DeviceId> deviceSubjects =
529 cfgService.getSubjects(DeviceId.class, NetconfDeviceConfig.class);
530 deviceSubjects.forEach(deviceId -> {
531 NetconfDeviceConfig config =
532 cfgService.getConfig(deviceId, NetconfDeviceConfig.class);
533 DeviceDescription deviceDescription = createDeviceRepresentation(deviceId, config);
534 storeDeviceKey(config.sshKey(), config.username(), config.password(), deviceId);
Andrea Campanella105736e2017-11-23 12:52:43 +0100535 checkAndUpdateDevice(deviceId, deviceDescription, false);
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200536 });
andreaeb70a942015-10-16 21:34:46 -0700537 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530538
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200539 private DeviceDescription createDeviceRepresentation(DeviceId deviceId, NetconfDeviceConfig config) {
540 Preconditions.checkNotNull(deviceId, ISNULL);
541 //Netconf configuration object
542 ChassisId cid = new ChassisId();
543 String ipAddress = config.ip().toString();
544 SparseAnnotations annotations = DefaultAnnotations.builder()
545 .set(IPADDRESS, ipAddress)
546 .set(PORT, String.valueOf(config.port()))
547 .set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase())
548 .build();
549 return new DefaultDeviceDescription(
550 deviceId.uri(),
551 Device.Type.SWITCH,
552 UNKNOWN, UNKNOWN,
553 UNKNOWN, UNKNOWN,
554 cid, false,
555 annotations);
556 }
557
558 private void storeDeviceKey(String sshKey, String username, String password, DeviceId deviceId) {
559 if (sshKey.equals("")) {
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600560 deviceKeyAdminService.addKey(
561 DeviceKey.createDeviceKeyUsingUsernamePassword(
562 DeviceKeyId.deviceKeyId(deviceId.toString()),
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200563 null, username, password));
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600564 } else {
565 deviceKeyAdminService.addKey(
566 DeviceKey.createDeviceKeyUsingSshKey(
567 DeviceKeyId.deviceKeyId(deviceId.toString()),
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200568 null, username, password,
569 sshKey));
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600570 }
571 }
572
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700573 private void initiateConnection(DeviceId deviceId, MastershipRole newRole) {
574 try {
575 if (isReachable(deviceId)) {
576 controller.connectDevice(deviceId);
577 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.MASTER);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700578 }
579 } catch (Exception e) {
580 if (deviceService.getDevice(deviceId) != null) {
581 providerService.deviceDisconnected(deviceId);
582 }
583 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(deviceId.toString()));
Ray Milkey986a47a2018-01-25 11:38:51 -0800584 throw new IllegalStateException(new NetconfException(
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200585 "Can't connect to NETCONF device " + deviceId, e));
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700586
587 }
588 }
589
590 private void discoverPorts(DeviceId deviceId) {
591 Device device = deviceService.getDevice(deviceId);
Andrea Campanella6c71a052016-04-22 11:56:31 -0700592 //TODO remove when PortDiscovery is removed from master
Ray Milkey640fedd2018-02-08 09:02:26 -0800593 if (device.is(DeviceDescriptionDiscovery.class)) {
Andrea Campanella6c71a052016-04-22 11:56:31 -0700594 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
595 device.as(DeviceDescriptionDiscovery.class);
596 providerService.updatePorts(deviceId,
597 deviceDescriptionDiscovery.discoverPortDetails());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700598 } else {
599 log.warn("No portGetter behaviour for device {}", deviceId);
600 }
Gaurav Agrawaldab4d772017-03-29 15:15:13 +0530601
602 // Port statistics discovery
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530603 updatePortStatistics(device);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700604 }
605
606 /**
607 * Return the DeviceId about the device containing the URI.
608 *
Andrea Campanella6c71a052016-04-22 11:56:31 -0700609 * @param ip IP address
Ray Milkeyd4334db2016-04-05 17:39:44 -0700610 * @param port port number
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700611 * @return DeviceId
612 */
613 public DeviceId getDeviceId(String ip, int port) {
614 try {
615 return DeviceId.deviceId(new URI(NETCONF, ip + ":" + port, null));
616 } catch (URISyntaxException e) {
617 throw new IllegalArgumentException("Unable to build deviceID for device "
618 + ip + ":" + port, e);
619 }
620 }
621
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200622
623 protected void translateConfig() {
624 NetconfProviderConfig cfg = cfgService.getConfig(appId, NetconfProviderConfig.class);
625 if (cfg != null) {
626 try {
627 cfg.getDevicesAddresses().forEach(addr -> {
628 DeviceId deviceId = getDeviceId(addr.ip().toString(), addr.port());
629 log.info("Translating config for device {}", deviceId);
630 if (cfgService.getConfig(deviceId, NetconfDeviceConfig.class) == null) {
631 ObjectMapper mapper = new ObjectMapper();
632 ObjectNode device = mapper.createObjectNode();
633 device.put("ip", addr.ip().toString());
634 device.put("port", addr.port());
635 device.put("username", addr.name());
636 device.put("password", addr.password());
637 device.put("sshkey", addr.sshkey());
638 cfgService.applyConfig(deviceId, NetconfDeviceConfig.class, device);
639 } else {
640 // This is a corner case where new updated config is
641 // pushed with old /app tree after an initial with the
642 // new device/ tree. Since old method will be deprecated
643 // it's ok to ignore
644 log.warn("Config for device {} already exists, ignoring", deviceId);
645 }
646
647 });
648 } catch (ConfigException e) {
649 log.error("Cannot read config error " + e);
650 }
651 }
652 }
653
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700654 /**
655 * Listener for configuration events.
656 */
andreaeb70a942015-10-16 21:34:46 -0700657 private class InternalNetworkConfigListener implements NetworkConfigListener {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530658
andreaeb70a942015-10-16 21:34:46 -0700659
660 @Override
661 public void event(NetworkConfigEvent event) {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200662 if (event.configClass().equals(NetconfDeviceConfig.class)) {
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700663 executor.execute(() -> connectDevice((NetconfDeviceConfig) event.config().get()));
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200664 } else {
665 log.warn("Injecting device via this Json is deprecated, " +
666 "please put configuration under devices/ as shown in the wiki");
667 translateConfig();
668 }
669
Sanjay Se8dcfee2015-04-23 10:07:08 +0530670 }
671
andreaeb70a942015-10-16 21:34:46 -0700672 @Override
673 public boolean isRelevant(NetworkConfigEvent event) {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200674 return (event.configClass().equals(NetconfDeviceConfig.class) ||
675 event.configClass().equals(NetconfProviderConfig.class)) &&
andreaeb70a942015-10-16 21:34:46 -0700676 (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
677 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530678 }
679 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700680
681 /**
682 * Listener for core device events.
683 */
684 private class InternalDeviceListener implements DeviceListener {
685 @Override
686 public void event(DeviceEvent event) {
687 if ((event.type() == DeviceEvent.Type.DEVICE_ADDED)) {
688 executor.execute(() -> discoverPorts(event.subject().id()));
689 } else if ((event.type() == DeviceEvent.Type.DEVICE_REMOVED)) {
690 log.debug("removing device {}", event.subject().id());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700691 controller.disconnectDevice(event.subject().id(), true);
692 }
693 }
694
695 @Override
696 public boolean isRelevant(DeviceEvent event) {
697 if (mastershipService.getMasterFor(event.subject().id()) == null) {
698 return true;
699 }
Andrea Campanella968f93f2017-06-08 11:09:28 +0200700 return (SCHEME_NAME.equalsIgnoreCase(event.subject().annotations().value(AnnotationKeys.PROTOCOL)) ||
701 (SCHEME_NAME.equalsIgnoreCase(event.subject().id().uri().getScheme()))) &&
Michele Santuari576f09c2016-09-28 14:20:00 +0200702 mastershipService.isLocalMaster(event.subject().id());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700703 }
704 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530705}