blob: 175e24446ab4f72088c1b3b8428cba07c4298b55 [file] [log] [blame]
Sanjay Se8dcfee2015-04-23 10:07:08 +05301/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
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;
36import org.onosproject.incubator.net.config.basics.ConfigException;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070037import org.onosproject.mastership.MastershipService;
Marc De Leenheerb0d131c2016-03-01 20:34:59 -080038import org.onosproject.net.AnnotationKeys;
andreaeb70a942015-10-16 21:34:46 -070039import org.onosproject.net.DefaultAnnotations;
Sanjay Se8dcfee2015-04-23 10:07:08 +053040import org.onosproject.net.Device;
41import org.onosproject.net.DeviceId;
42import org.onosproject.net.MastershipRole;
Saurav Dasa2d37502016-03-25 17:50:40 -070043import org.onosproject.net.PortNumber;
andreaeb70a942015-10-16 21:34:46 -070044import org.onosproject.net.SparseAnnotations;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -080045import org.onosproject.net.behaviour.PortDiscovery;
andreaeb70a942015-10-16 21:34:46 -070046import org.onosproject.net.config.ConfigFactory;
47import org.onosproject.net.config.NetworkConfigEvent;
48import org.onosproject.net.config.NetworkConfigListener;
49import org.onosproject.net.config.NetworkConfigRegistry;
Andrea Campanella34cf65c2017-04-12 13:51:32 +020050import org.onosproject.net.config.basics.SubjectFactories;
Sanjay Se8dcfee2015-04-23 10:07:08 +053051import org.onosproject.net.device.DefaultDeviceDescription;
52import org.onosproject.net.device.DeviceDescription;
Andrea Campanella6c71a052016-04-22 11:56:31 -070053import org.onosproject.net.device.DeviceDescriptionDiscovery;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070054import org.onosproject.net.device.DeviceEvent;
55import org.onosproject.net.device.DeviceListener;
Sanjay Se8dcfee2015-04-23 10:07:08 +053056import org.onosproject.net.device.DeviceProvider;
57import org.onosproject.net.device.DeviceProviderRegistry;
58import org.onosproject.net.device.DeviceProviderService;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -080059import org.onosproject.net.device.DeviceService;
Andrea Campanellac3627842017-04-04 18:06:54 +020060import org.onosproject.net.device.PortStatistics;
Gaurav Agrawaldab4d772017-03-29 15:15:13 +053061import org.onosproject.net.device.PortStatisticsDiscovery;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070062import org.onosproject.net.key.DeviceKey;
63import org.onosproject.net.key.DeviceKeyAdminService;
64import org.onosproject.net.key.DeviceKeyId;
Sanjay Se8dcfee2015-04-23 10:07:08 +053065import org.onosproject.net.provider.AbstractProvider;
66import org.onosproject.net.provider.ProviderId;
andreaeb70a942015-10-16 21:34:46 -070067import org.onosproject.netconf.NetconfController;
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -070068import org.onosproject.netconf.NetconfDevice;
andreaeb70a942015-10-16 21:34:46 -070069import org.onosproject.netconf.NetconfDeviceListener;
Andrea Campanella8b1cb672016-01-25 13:58:58 -080070import org.onosproject.netconf.NetconfException;
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -070071import org.onosproject.netconf.config.NetconfDeviceConfig;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -070072import org.osgi.service.component.ComponentContext;
Sanjay Se8dcfee2015-04-23 10:07:08 +053073import org.slf4j.Logger;
74
Andrea Campanella087ceb92015-12-07 09:58:34 -080075import java.io.IOException;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070076import java.net.Socket;
77import java.net.URI;
78import java.net.URISyntaxException;
79import java.util.Arrays;
Andrea Campanellac3627842017-04-04 18:06:54 +020080import java.util.Collection;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -070081import java.util.Dictionary;
Andrea Campanella34cf65c2017-04-12 13:51:32 +020082import java.util.List;
mskala832d0472017-06-09 16:31:42 +020083import java.util.Map;
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -070084import java.util.Optional;
Andrea Campanella34cf65c2017-04-12 13:51:32 +020085import java.util.Set;
mskala832d0472017-06-09 16:31:42 +020086import java.util.concurrent.ConcurrentHashMap;
Andrea Campanella5c999e22016-03-01 15:12:53 -080087import java.util.concurrent.ExecutorService;
88import java.util.concurrent.Executors;
helenyrwufd296b62016-06-22 17:43:02 -070089import java.util.concurrent.ScheduledExecutorService;
90import java.util.concurrent.ScheduledFuture;
91import java.util.concurrent.TimeUnit;
mskala832d0472017-06-09 16:31:42 +020092import java.util.concurrent.atomic.AtomicInteger;
andreaeb70a942015-10-16 21:34:46 -070093
Yuta HIGUCHI1624df12016-07-21 16:54:33 -070094import static java.util.concurrent.Executors.newScheduledThreadPool;
Andrea Campanella5c999e22016-03-01 15:12:53 -080095import static org.onlab.util.Tools.groupedThreads;
andreaeb70a942015-10-16 21:34:46 -070096import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
97import static org.slf4j.LoggerFactory.getLogger;
98
Sanjay Se8dcfee2015-04-23 10:07:08 +053099/**
andreaeb70a942015-10-16 21:34:46 -0700100 * Provider which uses an NETCONF controller to detect device.
Sanjay Se8dcfee2015-04-23 10:07:08 +0530101 */
102@Component(immediate = true)
103public class NetconfDeviceProvider extends AbstractProvider
104 implements DeviceProvider {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700105
andreaeb70a942015-10-16 21:34:46 -0700106 private final Logger log = getLogger(getClass());
Sanjay Se8dcfee2015-04-23 10:07:08 +0530107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected DeviceProviderRegistry providerRegistry;
110
andreaeb70a942015-10-16 21:34:46 -0700111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella101417d2015-12-11 17:58:07 -0800112 protected NetconfController controller;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
andreaeb70a942015-10-16 21:34:46 -0700115 protected NetworkConfigRegistry cfgService;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530116
Thomas Vachuskad6811712015-04-29 21:37:04 -0700117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
andreaeb70a942015-10-16 21:34:46 -0700118 protected CoreService coreService;
Thomas Vachuskad6811712015-04-29 21:37:04 -0700119
Aaron Kruglikov17b4c852016-01-15 16:37:04 -0800120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700121 protected DeviceService deviceService;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -0800122
123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700124 protected DeviceKeyAdminService deviceKeyAdminService;
125
126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
127 protected MastershipService mastershipService;
128
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700129 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
130 protected ComponentConfigService componentConfigService;
131
132
Michele Santuari576f09c2016-09-28 14:20:00 +0200133 protected static final String APP_NAME = "org.onosproject.netconf";
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200134 protected static final String SCHEME_NAME = "netconf";
Andrea Campanella101417d2015-12-11 17:58:07 -0800135 private static final String DEVICE_PROVIDER_PACKAGE = "org.onosproject.netconf.provider.device";
136 private static final String UNKNOWN = "unknown";
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700137 protected static final String ISNULL = "NetconfDeviceInfo is null";
138 private static final String IPADDRESS = "ipaddress";
139 private static final String NETCONF = "netconf";
140 private static final String PORT = "port";
helenyrwufd296b62016-06-22 17:43:02 -0700141 private static final int CORE_POOL_SIZE = 10;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700142
Thomas Vachuskadb29dcf2017-03-31 11:26:19 -0700143 private static final int DEFAULT_POLL_FREQUENCY_SECONDS = 30;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700144 @Property(name = "pollFrequency", intValue = DEFAULT_POLL_FREQUENCY_SECONDS,
145 label = "Configure poll frequency for port status and statistics; " +
Thomas Vachuskadb29dcf2017-03-31 11:26:19 -0700146 "default is 30 sec")
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700147 private int pollFrequency = DEFAULT_POLL_FREQUENCY_SECONDS;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530148
mskala832d0472017-06-09 16:31:42 +0200149 private static final int DEFAULT_MAX_RETRIES = 5;
150 @Property(name = "maxRetries", intValue = DEFAULT_MAX_RETRIES,
151 label = "Configure maximum allowed number of retries for obtaining list of ports; " +
152 "default is 5 times")
153 private int maxRetries = DEFAULT_MAX_RETRIES;
154
Michal Mach26a90fa2017-06-07 11:12:46 +0200155 protected ExecutorService executor =
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700156 Executors.newFixedThreadPool(5, groupedThreads("onos/netconfdeviceprovider",
157 "device-installer-%d", log));
Yuta HIGUCHI1624df12016-07-21 16:54:33 -0700158 protected ScheduledExecutorService connectionExecutor
159 = newScheduledThreadPool(CORE_POOL_SIZE,
160 groupedThreads("onos/netconfdeviceprovider",
161 "connection-executor-%d", log));
Andrea Campanella5c999e22016-03-01 15:12:53 -0800162
Michele Santuari576f09c2016-09-28 14:20:00 +0200163 protected DeviceProviderService providerService;
andreaeb70a942015-10-16 21:34:46 -0700164 private NetconfDeviceListener innerNodeListener = new InnerNetconfDeviceListener();
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700165 private InternalDeviceListener deviceListener = new InternalDeviceListener();
mskala832d0472017-06-09 16:31:42 +0200166 private final Map<DeviceId, AtomicInteger> retriedPortDiscoveryMap = new ConcurrentHashMap<>();
Michele Santuari576f09c2016-09-28 14:20:00 +0200167 protected ScheduledFuture<?> scheduledTask;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530168
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200169 protected final List<ConfigFactory> factories = ImmutableList.of(
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700170 // TODO consider moving Config registration to NETCONF ctl bundle
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200171 new ConfigFactory<DeviceId, NetconfDeviceConfig>(
172 SubjectFactories.DEVICE_SUBJECT_FACTORY,
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700173 NetconfDeviceConfig.class, NetconfDeviceConfig.CONFIG_KEY) {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200174 @Override
175 public NetconfDeviceConfig createConfig() {
176 return new NetconfDeviceConfig();
177 }
178 },
andreaeb70a942015-10-16 21:34:46 -0700179 new ConfigFactory<ApplicationId, NetconfProviderConfig>(APP_SUBJECT_FACTORY,
180 NetconfProviderConfig.class,
Palash Kalac3ffad92017-06-09 21:18:19 +0900181 "netconf_devices",
andreaeb70a942015-10-16 21:34:46 -0700182 true) {
183 @Override
184 public NetconfProviderConfig createConfig() {
185 return new NetconfProviderConfig();
186 }
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200187 });
188
Michele Santuari576f09c2016-09-28 14:20:00 +0200189 protected final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
andreaeb70a942015-10-16 21:34:46 -0700190 private ApplicationId appId;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700191 private boolean active;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530192
Sanjay Se8dcfee2015-04-23 10:07:08 +0530193
194 @Activate
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700195 public void activate(ComponentContext context) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700196 active = true;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700197 componentConfigService.registerProperties(getClass());
Sanjay Se8dcfee2015-04-23 10:07:08 +0530198 providerService = providerRegistry.register(this);
Andrea Campanella101417d2015-12-11 17:58:07 -0800199 appId = coreService.registerApplication(APP_NAME);
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200200 factories.forEach(cfgService::registerConfigFactory);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700201 cfgService.addListener(cfgListener);
andreaeb70a942015-10-16 21:34:46 -0700202 controller.addDeviceListener(innerNodeListener);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700203 deviceService.addListener(deviceListener);
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200204 translateConfig();
Andrea Campanella7d8449b2016-03-02 10:16:42 -0800205 executor.execute(NetconfDeviceProvider.this::connectDevices);
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700206 modified(context);
Thomas Vachuskad6811712015-04-29 21:37:04 -0700207 log.info("Started");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530208 }
209
andreaeb70a942015-10-16 21:34:46 -0700210
Sanjay Se8dcfee2015-04-23 10:07:08 +0530211 @Deactivate
andreaeb70a942015-10-16 21:34:46 -0700212 public void deactivate() {
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700213 componentConfigService.unregisterProperties(getClass(), false);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700214 deviceService.removeListener(deviceListener);
215 active = false;
216 controller.getNetconfDevices().forEach(id -> {
217 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(id.toString()));
218 controller.disconnectDevice(id, true);
219 });
Andrea Campanella86294db2016-03-07 11:42:49 -0800220 controller.removeDeviceListener(innerNodeListener);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700221 deviceService.removeListener(deviceListener);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530222 providerRegistry.unregister(this);
223 providerService = null;
mskala832d0472017-06-09 16:31:42 +0200224 retriedPortDiscoveryMap.clear();
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200225 factories.forEach(cfgService::unregisterConfigFactory);
helenyrwufd296b62016-06-22 17:43:02 -0700226 scheduledTask.cancel(true);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700227 executor.shutdown();
Sanjay Seb5eebb2015-04-24 15:44:50 +0530228 log.info("Stopped");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530229 }
230
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700231
232 @Modified
233 public void modified(ComponentContext context) {
234 if (context != null) {
235 Dictionary<?, ?> properties = context.getProperties();
236 pollFrequency = Tools.getIntegerProperty(properties, "pollFrequency",
237 DEFAULT_POLL_FREQUENCY_SECONDS);
238 log.info("Configured. Poll frequency is configured to {} seconds", pollFrequency);
mskala832d0472017-06-09 16:31:42 +0200239
240 maxRetries = Tools.getIntegerProperty(properties, "maxRetries",
241 DEFAULT_MAX_RETRIES);
242 log.info("Configured. Number of retries is configured to {} times", maxRetries);
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700243 }
244 if (scheduledTask != null) {
245 scheduledTask.cancel(false);
246 }
247 scheduledTask = schedulePolling();
248 }
249
andreaeb70a942015-10-16 21:34:46 -0700250 public NetconfDeviceProvider() {
Andrea Campanella101417d2015-12-11 17:58:07 -0800251 super(new ProviderId(SCHEME_NAME, DEVICE_PROVIDER_PACKAGE));
Sanjay Se8dcfee2015-04-23 10:07:08 +0530252 }
253
helenyrwufd296b62016-06-22 17:43:02 -0700254 // Checks connection to devices in the config file
255 // every DEFAULT_POLL_FREQUENCY_SECONDS seconds.
256 private ScheduledFuture schedulePolling() {
Yuta HIGUCHIc2b82e32017-03-10 14:33:41 -0800257 return connectionExecutor.scheduleAtFixedRate(exceptionSafe(this::checkAndUpdateDevices),
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700258 pollFrequency / 10,
259 pollFrequency, TimeUnit.SECONDS);
helenyrwufd296b62016-06-22 17:43:02 -0700260 }
261
Yuta HIGUCHIc2b82e32017-03-10 14:33:41 -0800262 private Runnable exceptionSafe(Runnable runnable) {
263 return new Runnable() {
264
265 @Override
266 public void run() {
267 try {
268 runnable.run();
269 } catch (Exception e) {
270 log.error("Unhandled Exception", e);
271 }
272 }
273 };
274 }
275
Sanjay Se8dcfee2015-04-23 10:07:08 +0530276 @Override
277 public void triggerProbe(DeviceId deviceId) {
andreaeb70a942015-10-16 21:34:46 -0700278 // TODO: This will be implemented later.
Yuta HIGUCHIe4cb8cf2017-05-01 22:18:28 -0700279 log.debug("Should be triggering probe on device {}", deviceId);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530280 }
281
282 @Override
283 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700284 if (active) {
285 switch (newRole) {
286 case MASTER:
287 initiateConnection(deviceId, newRole);
288 log.debug("Accepting mastership role change to {} for device {}", newRole, deviceId);
289 break;
290 case STANDBY:
291 controller.disconnectDevice(deviceId, false);
292 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.STANDBY);
293 //else no-op
294 break;
295 case NONE:
296 controller.disconnectDevice(deviceId, false);
297 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
298 break;
299 default:
300 log.error("Unimplemented Mastership state : {}", newRole);
301
302 }
303 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530304 }
305
306 @Override
307 public boolean isReachable(DeviceId deviceId) {
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700308
309 boolean sessionExists =
310 Optional.ofNullable(controller.getDevicesMap().get(deviceId))
311 .map(NetconfDevice::isActive)
312 .orElse(false);
313 if (sessionExists) {
314 return true;
315 }
316
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700317 //FIXME this is a workaround util device state is shared
318 // between controller instances.
319 Device device = deviceService.getDevice(deviceId);
320 String ip;
321 int port;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700322 if (device != null) {
323 ip = device.annotations().value(IPADDRESS);
324 port = Integer.parseInt(device.annotations().value(PORT));
325 } else {
326 String[] info = deviceId.toString().split(":");
327 if (info.length == 3) {
328 ip = info[1];
329 port = Integer.parseInt(info[2]);
330 } else {
331 ip = Arrays.asList(info).stream().filter(el -> !el.equals(info[0])
332 && !el.equals(info[info.length - 1]))
333 .reduce((t, u) -> t + ":" + u)
334 .get();
335 log.debug("ip v6 {}", ip);
336 port = Integer.parseInt(info[info.length - 1]);
337 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530338 }
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700339 // FIXME just opening TCP session probably is not the appropriate
340 // method to test reachability.
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700341 //test connection to device opening a socket to it.
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700342 log.debug("Testing reachability for {}:{}", ip, port);
Yuta HIGUCHI70c21472017-04-20 20:40:46 -0700343 try (Socket socket = new Socket(ip, port)) {
Yuta HIGUCHI0454d702017-03-17 10:08:38 -0700344 log.debug("rechability of {}, {}, {}", deviceId, socket.isConnected(), !socket.isClosed());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700345 return socket.isConnected() && !socket.isClosed();
346 } catch (IOException e) {
347 log.info("Device {} is not reachable", deviceId);
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700348 log.debug(" error details", e);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700349 return false;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700350 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530351 }
352
Saurav Dasa2d37502016-03-25 17:50:40 -0700353 @Override
354 public void changePortState(DeviceId deviceId, PortNumber portNumber,
355 boolean enable) {
356 // TODO if required
357 }
358
andreaeb70a942015-10-16 21:34:46 -0700359 private class InnerNetconfDeviceListener implements NetconfDeviceListener {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530360
Andrea Campanella101417d2015-12-11 17:58:07 -0800361
andreaeb70a942015-10-16 21:34:46 -0700362 @Override
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700363 public void deviceAdded(DeviceId deviceId) {
364 //no-op
365 log.debug("Netconf device {} added to Netconf subController", deviceId);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530366 }
367
368 @Override
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700369 public void deviceRemoved(DeviceId deviceId) {
370 Preconditions.checkNotNull(deviceId, ISNULL);
helenyrwufd296b62016-06-22 17:43:02 -0700371
372 if (deviceService.getDevice(deviceId) != null) {
373 providerService.deviceDisconnected(deviceId);
mskala832d0472017-06-09 16:31:42 +0200374 retriedPortDiscoveryMap.remove(deviceId);
helenyrwufd296b62016-06-22 17:43:02 -0700375 log.debug("Netconf device {} removed from Netconf subController", deviceId);
376 } else {
377 log.warn("Netconf device {} does not exist in the store, " +
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530378 "it may already have been removed", deviceId);
helenyrwufd296b62016-06-22 17:43:02 -0700379 }
andreaeb70a942015-10-16 21:34:46 -0700380 }
381 }
382
andreaeb70a942015-10-16 21:34:46 -0700383 private void connectDevices() {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200384 Set<DeviceId> deviceSubjects =
385 cfgService.getSubjects(DeviceId.class, NetconfDeviceConfig.class);
386 deviceSubjects.forEach(deviceId -> {
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700387 connectDevice(cfgService.getConfig(deviceId, NetconfDeviceConfig.class));
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200388 });
helenyrwufd296b62016-06-22 17:43:02 -0700389 }
390
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700391
392 private void connectDevice(NetconfDeviceConfig config) {
393 if (config == null) {
394 return;
395 }
396 DeviceId deviceId = config.subject();
397 if (!deviceId.uri().getScheme().equals(SCHEME_NAME)) {
398 // not under my scheme, skipping
399 log.trace("{} not my scheme, skipping", deviceId);
400 return;
401 }
402 DeviceDescription deviceDescription = createDeviceRepresentation(deviceId, config);
403 log.debug("Connecting NETCONF device {}, on {}:{} with username {}",
404 deviceId, config.ip(), config.port(), config.username());
405 storeDeviceKey(config.sshKey(), config.username(), config.password(), deviceId);
mskala832d0472017-06-09 16:31:42 +0200406 retriedPortDiscoveryMap.putIfAbsent(deviceId, new AtomicInteger(0));
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700407 if (deviceService.getDevice(deviceId) == null) {
408 providerService.deviceConnected(deviceId, deviceDescription);
409 }
410 try {
411 checkAndUpdateDevice(deviceId, deviceDescription);
412 } catch (Exception e) {
413 log.error("Unhandled exception checking {}", deviceId, e);
414 }
415 }
416
helenyrwufd296b62016-06-22 17:43:02 -0700417 private void checkAndUpdateDevice(DeviceId deviceId, DeviceDescription deviceDescription) {
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530418 Device device = deviceService.getDevice(deviceId);
419 if (device == null) {
Andrea Campanella7bbe7b12017-05-03 16:03:38 -0700420 log.debug("Device {} has not been added to store, " +
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200421 "since it's not reachable", deviceId);
helenyrwufd296b62016-06-22 17:43:02 -0700422 } else {
423 boolean isReachable = isReachable(deviceId);
424 if (isReachable && !deviceService.isAvailable(deviceId)) {
Konstantinos Kanonakis4d67dd82016-08-05 12:18:52 -0500425 if (device.is(DeviceDescriptionDiscovery.class)) {
426 if (mastershipService.isLocalMaster(deviceId)) {
427 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
428 device.as(DeviceDescriptionDiscovery.class);
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200429 DeviceDescription updatedDeviceDescription =
430 deviceDescriptionDiscovery.discoverDeviceDetails();
Michele Santuari00cc1f72016-09-08 17:05:24 +0200431 if (updatedDeviceDescription != null &&
432 !descriptionEquals(device, updatedDeviceDescription)) {
433 providerService.deviceConnected(
434 deviceId, new DefaultDeviceDescription(
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200435 updatedDeviceDescription, true,
436 updatedDeviceDescription.annotations()));
Michele Santuari576f09c2016-09-28 14:20:00 +0200437 } else if (updatedDeviceDescription == null) {
438 providerService.deviceConnected(
439 deviceId, new DefaultDeviceDescription(
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200440 deviceDescription, true,
441 deviceDescription.annotations()));
Michele Santuari00cc1f72016-09-08 17:05:24 +0200442 }
Konstantinos Kanonakis4d67dd82016-08-05 12:18:52 -0500443 }
444 } else {
Michele Santuari576f09c2016-09-28 14:20:00 +0200445 log.warn("No DeviceDescriptionDiscovery behaviour for device {} " +
446 "using DefaultDeviceDescription", deviceId);
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530447 providerService.deviceConnected(
448 deviceId, new DefaultDeviceDescription(
Michele Santuari576f09c2016-09-28 14:20:00 +0200449 deviceDescription, true, deviceDescription.annotations()));
Konstantinos Kanonakis4d67dd82016-08-05 12:18:52 -0500450 }
helenyrwufd296b62016-06-22 17:43:02 -0700451 } else if (!isReachable && deviceService.isAvailable(deviceId)) {
452 providerService.deviceDisconnected(deviceId);
Vidyashree Rama229554a2017-04-14 15:24:45 +0530453 } else if (isReachable && deviceService.isAvailable(deviceId) &&
454 mastershipService.isLocalMaster(deviceId)) {
mskala832d0472017-06-09 16:31:42 +0200455
456 //if ports are not discovered, retry the discovery
457 if (deviceService.getPorts(deviceId).isEmpty() &&
458 retriedPortDiscoveryMap.get(deviceId).getAndIncrement() < maxRetries) {
459 discoverPorts(deviceId);
460 }
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530461 updatePortStatistics(device);
helenyrwufd296b62016-06-22 17:43:02 -0700462 }
463 }
464 }
465
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530466 private void updatePortStatistics(Device device) {
467 if (device.is(PortStatisticsDiscovery.class)) {
468 PortStatisticsDiscovery d = device.as(PortStatisticsDiscovery.class);
Andrea Campanellac3627842017-04-04 18:06:54 +0200469 Collection<PortStatistics> portStatistics = d.discoverPortStatistics();
470 if (portStatistics != null) {
471 providerService.updatePortStatistics(device.id(),
472 portStatistics);
473 }
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530474 } else {
Yuta HIGUCHIe4cb8cf2017-05-01 22:18:28 -0700475 log.debug("No port statistics getter behaviour for device {}",
476 device.id());
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530477 }
478 }
479
Michele Santuari00cc1f72016-09-08 17:05:24 +0200480 private boolean descriptionEquals(Device device, DeviceDescription updatedDeviceDescription) {
Yuta HIGUCHIf381fc72017-01-03 10:39:36 -0800481 return Objects.equal(device.id().uri(), updatedDeviceDescription.deviceUri())
Michele Santuari00cc1f72016-09-08 17:05:24 +0200482 && Objects.equal(device.type(), updatedDeviceDescription.type())
483 && Objects.equal(device.manufacturer(), updatedDeviceDescription.manufacturer())
484 && Objects.equal(device.hwVersion(), updatedDeviceDescription.hwVersion())
485 && Objects.equal(device.swVersion(), updatedDeviceDescription.swVersion())
486 && Objects.equal(device.serialNumber(), updatedDeviceDescription.serialNumber())
487 && Objects.equal(device.chassisId(), updatedDeviceDescription.chassisId())
488 && Objects.equal(device.annotations(), updatedDeviceDescription.annotations());
489 }
490
helenyrwufd296b62016-06-22 17:43:02 -0700491 private void checkAndUpdateDevices() {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200492 Set<DeviceId> deviceSubjects =
493 cfgService.getSubjects(DeviceId.class, NetconfDeviceConfig.class);
494 deviceSubjects.forEach(deviceId -> {
495 NetconfDeviceConfig config =
496 cfgService.getConfig(deviceId, NetconfDeviceConfig.class);
497 DeviceDescription deviceDescription = createDeviceRepresentation(deviceId, config);
498 storeDeviceKey(config.sshKey(), config.username(), config.password(), deviceId);
499 checkAndUpdateDevice(deviceId, deviceDescription);
500 });
andreaeb70a942015-10-16 21:34:46 -0700501 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530502
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200503 private DeviceDescription createDeviceRepresentation(DeviceId deviceId, NetconfDeviceConfig config) {
504 Preconditions.checkNotNull(deviceId, ISNULL);
505 //Netconf configuration object
506 ChassisId cid = new ChassisId();
507 String ipAddress = config.ip().toString();
508 SparseAnnotations annotations = DefaultAnnotations.builder()
509 .set(IPADDRESS, ipAddress)
510 .set(PORT, String.valueOf(config.port()))
511 .set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase())
512 .build();
513 return new DefaultDeviceDescription(
514 deviceId.uri(),
515 Device.Type.SWITCH,
516 UNKNOWN, UNKNOWN,
517 UNKNOWN, UNKNOWN,
518 cid, false,
519 annotations);
520 }
521
522 private void storeDeviceKey(String sshKey, String username, String password, DeviceId deviceId) {
523 if (sshKey.equals("")) {
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600524 deviceKeyAdminService.addKey(
525 DeviceKey.createDeviceKeyUsingUsernamePassword(
526 DeviceKeyId.deviceKeyId(deviceId.toString()),
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200527 null, username, password));
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600528 } else {
529 deviceKeyAdminService.addKey(
530 DeviceKey.createDeviceKeyUsingSshKey(
531 DeviceKeyId.deviceKeyId(deviceId.toString()),
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200532 null, username, password,
533 sshKey));
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600534 }
535 }
536
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700537 private void initiateConnection(DeviceId deviceId, MastershipRole newRole) {
538 try {
539 if (isReachable(deviceId)) {
540 controller.connectDevice(deviceId);
541 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.MASTER);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700542 }
543 } catch (Exception e) {
544 if (deviceService.getDevice(deviceId) != null) {
545 providerService.deviceDisconnected(deviceId);
546 }
547 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(deviceId.toString()));
548 throw new RuntimeException(new NetconfException(
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200549 "Can't connect to NETCONF device " + deviceId, e));
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700550
551 }
552 }
553
554 private void discoverPorts(DeviceId deviceId) {
555 Device device = deviceService.getDevice(deviceId);
Andrea Campanella6c71a052016-04-22 11:56:31 -0700556 //TODO remove when PortDiscovery is removed from master
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700557 if (device.is(PortDiscovery.class)) {
558 PortDiscovery portConfig = device.as(PortDiscovery.class);
559 providerService.updatePorts(deviceId,
560 portConfig.getPorts());
Andrea Campanella6c71a052016-04-22 11:56:31 -0700561 } else if (device.is(DeviceDescriptionDiscovery.class)) {
562 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
563 device.as(DeviceDescriptionDiscovery.class);
564 providerService.updatePorts(deviceId,
565 deviceDescriptionDiscovery.discoverPortDetails());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700566 } else {
567 log.warn("No portGetter behaviour for device {}", deviceId);
568 }
Gaurav Agrawaldab4d772017-03-29 15:15:13 +0530569
570 // Port statistics discovery
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530571 updatePortStatistics(device);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700572 }
573
574 /**
575 * Return the DeviceId about the device containing the URI.
576 *
Andrea Campanella6c71a052016-04-22 11:56:31 -0700577 * @param ip IP address
Ray Milkeyd4334db2016-04-05 17:39:44 -0700578 * @param port port number
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700579 * @return DeviceId
580 */
581 public DeviceId getDeviceId(String ip, int port) {
582 try {
583 return DeviceId.deviceId(new URI(NETCONF, ip + ":" + port, null));
584 } catch (URISyntaxException e) {
585 throw new IllegalArgumentException("Unable to build deviceID for device "
586 + ip + ":" + port, e);
587 }
588 }
589
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200590
591 protected void translateConfig() {
592 NetconfProviderConfig cfg = cfgService.getConfig(appId, NetconfProviderConfig.class);
593 if (cfg != null) {
594 try {
595 cfg.getDevicesAddresses().forEach(addr -> {
596 DeviceId deviceId = getDeviceId(addr.ip().toString(), addr.port());
597 log.info("Translating config for device {}", deviceId);
598 if (cfgService.getConfig(deviceId, NetconfDeviceConfig.class) == null) {
599 ObjectMapper mapper = new ObjectMapper();
600 ObjectNode device = mapper.createObjectNode();
601 device.put("ip", addr.ip().toString());
602 device.put("port", addr.port());
603 device.put("username", addr.name());
604 device.put("password", addr.password());
605 device.put("sshkey", addr.sshkey());
606 cfgService.applyConfig(deviceId, NetconfDeviceConfig.class, device);
607 } else {
608 // This is a corner case where new updated config is
609 // pushed with old /app tree after an initial with the
610 // new device/ tree. Since old method will be deprecated
611 // it's ok to ignore
612 log.warn("Config for device {} already exists, ignoring", deviceId);
613 }
614
615 });
616 } catch (ConfigException e) {
617 log.error("Cannot read config error " + e);
618 }
619 }
620 }
621
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700622 /**
623 * Listener for configuration events.
624 */
andreaeb70a942015-10-16 21:34:46 -0700625 private class InternalNetworkConfigListener implements NetworkConfigListener {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530626
andreaeb70a942015-10-16 21:34:46 -0700627
628 @Override
629 public void event(NetworkConfigEvent event) {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200630 if (event.configClass().equals(NetconfDeviceConfig.class)) {
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700631 executor.execute(() -> connectDevice((NetconfDeviceConfig) event.config().get()));
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200632 } else {
633 log.warn("Injecting device via this Json is deprecated, " +
634 "please put configuration under devices/ as shown in the wiki");
635 translateConfig();
636 }
637
Sanjay Se8dcfee2015-04-23 10:07:08 +0530638 }
639
andreaeb70a942015-10-16 21:34:46 -0700640 @Override
641 public boolean isRelevant(NetworkConfigEvent event) {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200642 return (event.configClass().equals(NetconfDeviceConfig.class) ||
643 event.configClass().equals(NetconfProviderConfig.class)) &&
andreaeb70a942015-10-16 21:34:46 -0700644 (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
645 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530646 }
647 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700648
649 /**
650 * Listener for core device events.
651 */
652 private class InternalDeviceListener implements DeviceListener {
653 @Override
654 public void event(DeviceEvent event) {
655 if ((event.type() == DeviceEvent.Type.DEVICE_ADDED)) {
656 executor.execute(() -> discoverPorts(event.subject().id()));
657 } else if ((event.type() == DeviceEvent.Type.DEVICE_REMOVED)) {
658 log.debug("removing device {}", event.subject().id());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700659 controller.disconnectDevice(event.subject().id(), true);
660 }
661 }
662
663 @Override
664 public boolean isRelevant(DeviceEvent event) {
665 if (mastershipService.getMasterFor(event.subject().id()) == null) {
666 return true;
667 }
Andrea Campanella968f93f2017-06-08 11:09:28 +0200668 return (SCHEME_NAME.equalsIgnoreCase(event.subject().annotations().value(AnnotationKeys.PROTOCOL)) ||
669 (SCHEME_NAME.equalsIgnoreCase(event.subject().id().uri().getScheme()))) &&
Michele Santuari576f09c2016-09-28 14:20:00 +0200670 mastershipService.isLocalMaster(event.subject().id());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700671 }
672 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530673}