blob: e54e90fc5be35a8104f1a12549b3c1c5482fd279 [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
Michele Santuari00cc1f72016-09-08 17:05:24 +020019import com.google.common.base.Objects;
andreaeb70a942015-10-16 21:34:46 -070020import com.google.common.base.Preconditions;
Andrea Campanella42e7b862018-09-21 11:56:48 +020021import com.google.common.util.concurrent.Striped;
Sanjay Se8dcfee2015-04-23 10:07:08 +053022import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Component;
24import org.apache.felix.scr.annotations.Deactivate;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -070025import org.apache.felix.scr.annotations.Modified;
26import org.apache.felix.scr.annotations.Property;
Sanjay Se8dcfee2015-04-23 10:07:08 +053027import org.apache.felix.scr.annotations.Reference;
28import org.apache.felix.scr.annotations.ReferenceCardinality;
29import org.onlab.packet.ChassisId;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -070030import org.onlab.util.Tools;
31import org.onosproject.cfg.ComponentConfigService;
andreaeb70a942015-10-16 21:34:46 -070032import org.onosproject.core.ApplicationId;
33import org.onosproject.core.CoreService;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070034import org.onosproject.mastership.MastershipService;
Marc De Leenheerb0d131c2016-03-01 20:34:59 -080035import org.onosproject.net.AnnotationKeys;
andreaeb70a942015-10-16 21:34:46 -070036import org.onosproject.net.DefaultAnnotations;
Sanjay Se8dcfee2015-04-23 10:07:08 +053037import org.onosproject.net.Device;
38import org.onosproject.net.DeviceId;
39import org.onosproject.net.MastershipRole;
Ray Milkey02710432018-02-13 17:08:28 -080040import org.onosproject.net.Port;
Saurav Dasa2d37502016-03-25 17:50:40 -070041import org.onosproject.net.PortNumber;
andreaeb70a942015-10-16 21:34:46 -070042import org.onosproject.net.SparseAnnotations;
Ray Milkey02710432018-02-13 17:08:28 -080043import org.onosproject.net.behaviour.PortAdmin;
andreaeb70a942015-10-16 21:34:46 -070044import org.onosproject.net.config.ConfigFactory;
45import org.onosproject.net.config.NetworkConfigEvent;
46import org.onosproject.net.config.NetworkConfigListener;
47import org.onosproject.net.config.NetworkConfigRegistry;
Andrea Campanella34cf65c2017-04-12 13:51:32 +020048import org.onosproject.net.config.basics.SubjectFactories;
Sanjay Se8dcfee2015-04-23 10:07:08 +053049import org.onosproject.net.device.DefaultDeviceDescription;
Andrea Campanella32813682017-10-23 15:29:24 +020050import org.onosproject.net.device.DefaultPortDescription;
Sanjay Se8dcfee2015-04-23 10:07:08 +053051import org.onosproject.net.device.DeviceDescription;
Andrea Campanella6c71a052016-04-22 11:56:31 -070052import org.onosproject.net.device.DeviceDescriptionDiscovery;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070053import org.onosproject.net.device.DeviceEvent;
54import org.onosproject.net.device.DeviceListener;
Sanjay Se8dcfee2015-04-23 10:07:08 +053055import org.onosproject.net.device.DeviceProvider;
56import org.onosproject.net.device.DeviceProviderRegistry;
57import org.onosproject.net.device.DeviceProviderService;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -080058import org.onosproject.net.device.DeviceService;
Andrea Campanellac3627842017-04-04 18:06:54 +020059import org.onosproject.net.device.PortStatistics;
Gaurav Agrawaldab4d772017-03-29 15:15:13 +053060import org.onosproject.net.device.PortStatisticsDiscovery;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070061import org.onosproject.net.key.DeviceKey;
62import org.onosproject.net.key.DeviceKeyAdminService;
63import org.onosproject.net.key.DeviceKeyId;
Sanjay Se8dcfee2015-04-23 10:07:08 +053064import org.onosproject.net.provider.AbstractProvider;
65import org.onosproject.net.provider.ProviderId;
andreaeb70a942015-10-16 21:34:46 -070066import org.onosproject.netconf.NetconfController;
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -070067import org.onosproject.netconf.NetconfDevice;
andreaeb70a942015-10-16 21:34:46 -070068import org.onosproject.netconf.NetconfDeviceListener;
Andrea Campanella8b1cb672016-01-25 13:58:58 -080069import org.onosproject.netconf.NetconfException;
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -070070import org.onosproject.netconf.config.NetconfDeviceConfig;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -070071import org.osgi.service.component.ComponentContext;
Sanjay Se8dcfee2015-04-23 10:07:08 +053072import org.slf4j.Logger;
73
Andrea Campanella087ceb92015-12-07 09:58:34 -080074import java.io.IOException;
Andrea Campanella42e7b862018-09-21 11:56:48 +020075import java.net.InetSocketAddress;
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;
mskala832d0472017-06-09 16:31:42 +020082import java.util.Map;
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -070083import java.util.Optional;
Andrea Campanella34cf65c2017-04-12 13:51:32 +020084import java.util.Set;
Andrea Campanella32813682017-10-23 15:29:24 +020085import java.util.concurrent.CompletableFuture;
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;
Andrea Campanella42e7b862018-09-21 11:56:48 +020093import java.util.concurrent.locks.Lock;
94import java.util.function.Supplier;
andreaeb70a942015-10-16 21:34:46 -070095
Yuta HIGUCHI1624df12016-07-21 16:54:33 -070096import static java.util.concurrent.Executors.newScheduledThreadPool;
Andrea Campanella5c999e22016-03-01 15:12:53 -080097import static org.onlab.util.Tools.groupedThreads;
andreaeb70a942015-10-16 21:34:46 -070098import static org.slf4j.LoggerFactory.getLogger;
99
Sanjay Se8dcfee2015-04-23 10:07:08 +0530100/**
andreaeb70a942015-10-16 21:34:46 -0700101 * Provider which uses an NETCONF controller to detect device.
Sanjay Se8dcfee2015-04-23 10:07:08 +0530102 */
103@Component(immediate = true)
104public class NetconfDeviceProvider extends AbstractProvider
105 implements DeviceProvider {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700106
andreaeb70a942015-10-16 21:34:46 -0700107 private final Logger log = getLogger(getClass());
Sanjay Se8dcfee2015-04-23 10:07:08 +0530108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
110 protected DeviceProviderRegistry providerRegistry;
111
andreaeb70a942015-10-16 21:34:46 -0700112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella101417d2015-12-11 17:58:07 -0800113 protected NetconfController controller;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
andreaeb70a942015-10-16 21:34:46 -0700116 protected NetworkConfigRegistry cfgService;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530117
Thomas Vachuskad6811712015-04-29 21:37:04 -0700118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
andreaeb70a942015-10-16 21:34:46 -0700119 protected CoreService coreService;
Thomas Vachuskad6811712015-04-29 21:37:04 -0700120
Aaron Kruglikov17b4c852016-01-15 16:37:04 -0800121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700122 protected DeviceService deviceService;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -0800123
124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700125 protected DeviceKeyAdminService deviceKeyAdminService;
126
127 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
128 protected MastershipService mastershipService;
129
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700130 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
131 protected ComponentConfigService componentConfigService;
132
133
Michele Santuari576f09c2016-09-28 14:20:00 +0200134 protected static final String APP_NAME = "org.onosproject.netconf";
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200135 protected static final String SCHEME_NAME = "netconf";
Andrea Campanella101417d2015-12-11 17:58:07 -0800136 private static final String DEVICE_PROVIDER_PACKAGE = "org.onosproject.netconf.provider.device";
137 private static final String UNKNOWN = "unknown";
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700138 protected static final String ISNULL = "NetconfDeviceInfo is null";
139 private static final String IPADDRESS = "ipaddress";
140 private static final String NETCONF = "netconf";
141 private static final String PORT = "port";
helenyrwufd296b62016-06-22 17:43:02 -0700142 private static final int CORE_POOL_SIZE = 10;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700143
Thomas Vachuskadb29dcf2017-03-31 11:26:19 -0700144 private static final int DEFAULT_POLL_FREQUENCY_SECONDS = 30;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700145 @Property(name = "pollFrequency", intValue = DEFAULT_POLL_FREQUENCY_SECONDS,
146 label = "Configure poll frequency for port status and statistics; " +
Thomas Vachuskadb29dcf2017-03-31 11:26:19 -0700147 "default is 30 sec")
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700148 private int pollFrequency = DEFAULT_POLL_FREQUENCY_SECONDS;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530149
mskala832d0472017-06-09 16:31:42 +0200150 private static final int DEFAULT_MAX_RETRIES = 5;
151 @Property(name = "maxRetries", intValue = DEFAULT_MAX_RETRIES,
152 label = "Configure maximum allowed number of retries for obtaining list of ports; " +
153 "default is 5 times")
154 private int maxRetries = DEFAULT_MAX_RETRIES;
155
Michal Mach26a90fa2017-06-07 11:12:46 +0200156 protected ExecutorService executor =
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700157 Executors.newFixedThreadPool(5, groupedThreads("onos/netconfdeviceprovider",
158 "device-installer-%d", log));
Yuta HIGUCHI1624df12016-07-21 16:54:33 -0700159 protected ScheduledExecutorService connectionExecutor
160 = newScheduledThreadPool(CORE_POOL_SIZE,
161 groupedThreads("onos/netconfdeviceprovider",
162 "connection-executor-%d", log));
Andrea Campanella5c999e22016-03-01 15:12:53 -0800163
Michele Santuari576f09c2016-09-28 14:20:00 +0200164 protected DeviceProviderService providerService;
andreaeb70a942015-10-16 21:34:46 -0700165 private NetconfDeviceListener innerNodeListener = new InnerNetconfDeviceListener();
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700166 private InternalDeviceListener deviceListener = new InternalDeviceListener();
mskala832d0472017-06-09 16:31:42 +0200167 private final Map<DeviceId, AtomicInteger> retriedPortDiscoveryMap = new ConcurrentHashMap<>();
Michele Santuari576f09c2016-09-28 14:20:00 +0200168 protected ScheduledFuture<?> scheduledTask;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530169
Ray Milkey02710432018-02-13 17:08:28 -0800170 protected final ConfigFactory factory =
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700171 // TODO consider moving Config registration to NETCONF ctl bundle
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200172 new ConfigFactory<DeviceId, NetconfDeviceConfig>(
173 SubjectFactories.DEVICE_SUBJECT_FACTORY,
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700174 NetconfDeviceConfig.class, NetconfDeviceConfig.CONFIG_KEY) {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200175 @Override
176 public NetconfDeviceConfig createConfig() {
177 return new NetconfDeviceConfig();
178 }
Ray Milkey02710432018-02-13 17:08:28 -0800179 };
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200180
Michele Santuari576f09c2016-09-28 14:20:00 +0200181 protected final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
andreaeb70a942015-10-16 21:34:46 -0700182 private ApplicationId appId;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700183 private boolean active;
Andrea Campanella42e7b862018-09-21 11:56:48 +0200184 private final Striped<Lock> deviceLocks = Striped.lock(30);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530185
Sanjay Se8dcfee2015-04-23 10:07:08 +0530186
187 @Activate
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700188 public void activate(ComponentContext context) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700189 active = true;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700190 componentConfigService.registerProperties(getClass());
Sanjay Se8dcfee2015-04-23 10:07:08 +0530191 providerService = providerRegistry.register(this);
Andrea Campanella101417d2015-12-11 17:58:07 -0800192 appId = coreService.registerApplication(APP_NAME);
Ray Milkey02710432018-02-13 17:08:28 -0800193 cfgService.registerConfigFactory(factory);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700194 cfgService.addListener(cfgListener);
andreaeb70a942015-10-16 21:34:46 -0700195 controller.addDeviceListener(innerNodeListener);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700196 deviceService.addListener(deviceListener);
Andrea Campanella7d8449b2016-03-02 10:16:42 -0800197 executor.execute(NetconfDeviceProvider.this::connectDevices);
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700198 modified(context);
Thomas Vachuskad6811712015-04-29 21:37:04 -0700199 log.info("Started");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530200 }
201
andreaeb70a942015-10-16 21:34:46 -0700202
Sanjay Se8dcfee2015-04-23 10:07:08 +0530203 @Deactivate
andreaeb70a942015-10-16 21:34:46 -0700204 public void deactivate() {
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700205 componentConfigService.unregisterProperties(getClass(), false);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700206 deviceService.removeListener(deviceListener);
207 active = false;
208 controller.getNetconfDevices().forEach(id -> {
209 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(id.toString()));
210 controller.disconnectDevice(id, true);
211 });
Andrea Campanella86294db2016-03-07 11:42:49 -0800212 controller.removeDeviceListener(innerNodeListener);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700213 deviceService.removeListener(deviceListener);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530214 providerRegistry.unregister(this);
215 providerService = null;
mskala832d0472017-06-09 16:31:42 +0200216 retriedPortDiscoveryMap.clear();
Ray Milkey02710432018-02-13 17:08:28 -0800217 cfgService.unregisterConfigFactory(factory);
helenyrwufd296b62016-06-22 17:43:02 -0700218 scheduledTask.cancel(true);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700219 executor.shutdown();
Sanjay Seb5eebb2015-04-24 15:44:50 +0530220 log.info("Stopped");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530221 }
222
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700223
224 @Modified
225 public void modified(ComponentContext context) {
226 if (context != null) {
227 Dictionary<?, ?> properties = context.getProperties();
228 pollFrequency = Tools.getIntegerProperty(properties, "pollFrequency",
229 DEFAULT_POLL_FREQUENCY_SECONDS);
230 log.info("Configured. Poll frequency is configured to {} seconds", pollFrequency);
mskala832d0472017-06-09 16:31:42 +0200231
232 maxRetries = Tools.getIntegerProperty(properties, "maxRetries",
233 DEFAULT_MAX_RETRIES);
234 log.info("Configured. Number of retries is configured to {} times", maxRetries);
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700235 }
236 if (scheduledTask != null) {
237 scheduledTask.cancel(false);
238 }
239 scheduledTask = schedulePolling();
240 }
241
andreaeb70a942015-10-16 21:34:46 -0700242 public NetconfDeviceProvider() {
Andrea Campanella101417d2015-12-11 17:58:07 -0800243 super(new ProviderId(SCHEME_NAME, DEVICE_PROVIDER_PACKAGE));
Sanjay Se8dcfee2015-04-23 10:07:08 +0530244 }
245
helenyrwufd296b62016-06-22 17:43:02 -0700246 // Checks connection to devices in the config file
247 // every DEFAULT_POLL_FREQUENCY_SECONDS seconds.
248 private ScheduledFuture schedulePolling() {
Yuta HIGUCHIc2b82e32017-03-10 14:33:41 -0800249 return connectionExecutor.scheduleAtFixedRate(exceptionSafe(this::checkAndUpdateDevices),
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700250 pollFrequency / 10,
251 pollFrequency, TimeUnit.SECONDS);
helenyrwufd296b62016-06-22 17:43:02 -0700252 }
253
Yuta HIGUCHIc2b82e32017-03-10 14:33:41 -0800254 private Runnable exceptionSafe(Runnable runnable) {
255 return new Runnable() {
256
257 @Override
258 public void run() {
259 try {
260 runnable.run();
261 } catch (Exception e) {
262 log.error("Unhandled Exception", e);
263 }
264 }
265 };
266 }
267
Sanjay Se8dcfee2015-04-23 10:07:08 +0530268 @Override
269 public void triggerProbe(DeviceId deviceId) {
andreaeb70a942015-10-16 21:34:46 -0700270 // TODO: This will be implemented later.
Yuta HIGUCHIe4cb8cf2017-05-01 22:18:28 -0700271 log.debug("Should be triggering probe on device {}", deviceId);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530272 }
273
274 @Override
275 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700276 if (active) {
277 switch (newRole) {
278 case MASTER:
Andrea Campanella42e7b862018-09-21 11:56:48 +0200279 withDeviceLock(
280 () -> initiateConnection(deviceId, newRole), deviceId).run();
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700281 log.debug("Accepting mastership role change to {} for device {}", newRole, deviceId);
282 break;
283 case STANDBY:
284 controller.disconnectDevice(deviceId, false);
285 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.STANDBY);
286 //else no-op
287 break;
288 case NONE:
289 controller.disconnectDevice(deviceId, false);
290 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
291 break;
292 default:
293 log.error("Unimplemented Mastership state : {}", newRole);
294
295 }
296 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530297 }
298
299 @Override
300 public boolean isReachable(DeviceId deviceId) {
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700301
302 boolean sessionExists =
303 Optional.ofNullable(controller.getDevicesMap().get(deviceId))
304 .map(NetconfDevice::isActive)
305 .orElse(false);
306 if (sessionExists) {
307 return true;
308 }
309
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700310 //FIXME this is a workaround util device state is shared
311 // between controller instances.
312 Device device = deviceService.getDevice(deviceId);
313 String ip;
314 int port;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700315 if (device != null) {
316 ip = device.annotations().value(IPADDRESS);
317 port = Integer.parseInt(device.annotations().value(PORT));
318 } else {
319 String[] info = deviceId.toString().split(":");
320 if (info.length == 3) {
321 ip = info[1];
322 port = Integer.parseInt(info[2]);
323 } else {
324 ip = Arrays.asList(info).stream().filter(el -> !el.equals(info[0])
325 && !el.equals(info[info.length - 1]))
326 .reduce((t, u) -> t + ":" + u)
327 .get();
328 log.debug("ip v6 {}", ip);
329 port = Integer.parseInt(info[info.length - 1]);
330 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530331 }
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700332 // FIXME just opening TCP session probably is not the appropriate
333 // method to test reachability.
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700334 //test connection to device opening a socket to it.
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700335 log.debug("Testing reachability for {}:{}", ip, port);
Andrea Campanella42e7b862018-09-21 11:56:48 +0200336 Socket socket = new Socket();
337 try {
338 socket.connect(new InetSocketAddress(ip, port), 1000);
Yuta HIGUCHI0454d702017-03-17 10:08:38 -0700339 log.debug("rechability of {}, {}, {}", deviceId, socket.isConnected(), !socket.isClosed());
Andrea Campanella42e7b862018-09-21 11:56:48 +0200340 boolean isConnected = socket.isConnected() && !socket.isClosed();
341 socket.close();
342 return isConnected;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700343 } catch (IOException e) {
344 log.info("Device {} is not reachable", deviceId);
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700345 log.debug(" error details", e);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700346 return false;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700347 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530348 }
349
Saurav Dasa2d37502016-03-25 17:50:40 -0700350 @Override
351 public void changePortState(DeviceId deviceId, PortNumber portNumber,
352 boolean enable) {
Andrea Campanella32813682017-10-23 15:29:24 +0200353 Device device = deviceService.getDevice(deviceId);
354 if (mastershipService.isLocalMaster(deviceId)) {
355 if (device.is(PortAdmin.class)) {
356 PortAdmin portAdmin =
357 device.as(PortAdmin.class);
358 CompletableFuture<Boolean> modified;
359 if (enable) {
360 modified = portAdmin.enable(portNumber);
361 } else {
362 modified = portAdmin.disable(portNumber);
363 }
364 modified.thenAccept(result -> {
365 if (result) {
366 Port port = deviceService.getPort(deviceId, portNumber);
367 //rebuilding port description with admin state changed.
368 providerService.portStatusChanged(deviceId,
Yuta HIGUCHI53e47962018-03-01 23:50:48 -0800369 DefaultPortDescription.builder()
370 .withPortNumber(portNumber)
371 .isEnabled(enable)
372 .isRemoved(false)
373 .type(port.type())
374 .portSpeed(port.portSpeed())
375 .annotations((SparseAnnotations) port.annotations())
376 .build());
Andrea Campanella32813682017-10-23 15:29:24 +0200377 } else {
378 log.warn("Your device {} port {} status can't be changed to {}",
379 deviceId, portNumber, enable);
380 }
381 });
382 } else {
383 log.warn("Device {} does not support Port Admin", deviceId);
384 }
385 } else {
386 log.debug("Not master but {}, not changing port state", mastershipService.getLocalRole(deviceId));
387 }
Saurav Dasa2d37502016-03-25 17:50:40 -0700388 }
389
Thomas Vachuska5b38dc02018-05-10 15:24:40 -0700390 @Override
391 public void triggerDisconnect(DeviceId deviceId) {
392 log.debug("Forcing disconnect for device {}", deviceId);
393 controller.disconnectDevice(deviceId, true);
394 }
395
andreaeb70a942015-10-16 21:34:46 -0700396 private class InnerNetconfDeviceListener implements NetconfDeviceListener {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530397
Andrea Campanella101417d2015-12-11 17:58:07 -0800398
andreaeb70a942015-10-16 21:34:46 -0700399 @Override
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700400 public void deviceAdded(DeviceId deviceId) {
401 //no-op
402 log.debug("Netconf device {} added to Netconf subController", deviceId);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530403 }
404
405 @Override
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700406 public void deviceRemoved(DeviceId deviceId) {
407 Preconditions.checkNotNull(deviceId, ISNULL);
helenyrwufd296b62016-06-22 17:43:02 -0700408
409 if (deviceService.getDevice(deviceId) != null) {
410 providerService.deviceDisconnected(deviceId);
mskala832d0472017-06-09 16:31:42 +0200411 retriedPortDiscoveryMap.remove(deviceId);
helenyrwufd296b62016-06-22 17:43:02 -0700412 log.debug("Netconf device {} removed from Netconf subController", deviceId);
413 } else {
414 log.warn("Netconf device {} does not exist in the store, " +
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530415 "it may already have been removed", deviceId);
helenyrwufd296b62016-06-22 17:43:02 -0700416 }
andreaeb70a942015-10-16 21:34:46 -0700417 }
418 }
419
andreaeb70a942015-10-16 21:34:46 -0700420 private void connectDevices() {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200421 Set<DeviceId> deviceSubjects =
422 cfgService.getSubjects(DeviceId.class, NetconfDeviceConfig.class);
423 deviceSubjects.forEach(deviceId -> {
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700424 connectDevice(cfgService.getConfig(deviceId, NetconfDeviceConfig.class));
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200425 });
helenyrwufd296b62016-06-22 17:43:02 -0700426 }
427
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700428
429 private void connectDevice(NetconfDeviceConfig config) {
430 if (config == null) {
431 return;
432 }
433 DeviceId deviceId = config.subject();
434 if (!deviceId.uri().getScheme().equals(SCHEME_NAME)) {
435 // not under my scheme, skipping
436 log.trace("{} not my scheme, skipping", deviceId);
437 return;
438 }
Andrea Campanella42e7b862018-09-21 11:56:48 +0200439 if (!isReachable(deviceId)) {
440 log.warn("Can't connect to device {}", deviceId);
441 return;
442 }
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700443 DeviceDescription deviceDescription = createDeviceRepresentation(deviceId, config);
444 log.debug("Connecting NETCONF device {}, on {}:{} with username {}",
445 deviceId, config.ip(), config.port(), config.username());
446 storeDeviceKey(config.sshKey(), config.username(), config.password(), deviceId);
Andrea Campanella105736e2017-11-23 12:52:43 +0100447 retriedPortDiscoveryMap.put(deviceId, new AtomicInteger(0));
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700448 if (deviceService.getDevice(deviceId) == null) {
449 providerService.deviceConnected(deviceId, deviceDescription);
450 }
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700451 }
452
Andrea Campanella105736e2017-11-23 12:52:43 +0100453 private void checkAndUpdateDevice(DeviceId deviceId, DeviceDescription deviceDescription, boolean newlyConnected) {
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530454 Device device = deviceService.getDevice(deviceId);
455 if (device == null) {
Andrea Campanella105736e2017-11-23 12:52:43 +0100456 log.debug("Device {} has not been added to store, since it's not reachable", deviceId);
457 return;
458 }
459 boolean isReachable = isReachable(deviceId);
460 if (!isReachable && deviceService.isAvailable(deviceId)) {
461 providerService.deviceDisconnected(deviceId);
462 return;
Andrea Campanella42e7b862018-09-21 11:56:48 +0200463 } else if (newlyConnected && mastershipService.isLocalMaster(deviceId)) {
Andrea Campanella105736e2017-11-23 12:52:43 +0100464 updateDeviceDescription(deviceId, deviceDescription, device);
465 }
466 if (isReachable && deviceService.isAvailable(deviceId) &&
467 mastershipService.isLocalMaster(deviceId)) {
468 //if ports are not discovered, retry the discovery
469 if (deviceService.getPorts(deviceId).isEmpty() &&
470 retriedPortDiscoveryMap.get(deviceId).getAndIncrement() < maxRetries) {
471 discoverPorts(deviceId);
472 }
473 updatePortStatistics(device);
474 }
475 }
476
477 private void updateDeviceDescription(DeviceId deviceId, DeviceDescription deviceDescription, Device device) {
478 if (device.is(DeviceDescriptionDiscovery.class)) {
479 if (mastershipService.isLocalMaster(deviceId)) {
480 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
481 device.as(DeviceDescriptionDiscovery.class);
482 DeviceDescription updatedDeviceDescription =
483 deviceDescriptionDiscovery.discoverDeviceDetails();
484 if (updatedDeviceDescription != null &&
485 !descriptionEquals(device, updatedDeviceDescription)) {
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530486 providerService.deviceConnected(
487 deviceId, new DefaultDeviceDescription(
Andrea Campanella105736e2017-11-23 12:52:43 +0100488 updatedDeviceDescription, true,
489 updatedDeviceDescription.annotations()));
Ray Milkey33306ba2018-09-20 13:27:25 -0700490 } else if (updatedDeviceDescription == null) {
Andrea Campanella105736e2017-11-23 12:52:43 +0100491 providerService.deviceConnected(
492 deviceId, new DefaultDeviceDescription(
493 deviceDescription, true,
494 deviceDescription.annotations()));
Konstantinos Kanonakis4d67dd82016-08-05 12:18:52 -0500495 }
helenyrwufd296b62016-06-22 17:43:02 -0700496 }
Andrea Campanella105736e2017-11-23 12:52:43 +0100497 } else {
498 log.warn("No DeviceDescriptionDiscovery behaviour for device {} " +
499 "using DefaultDeviceDescription", deviceId);
500 providerService.deviceConnected(
501 deviceId, new DefaultDeviceDescription(
502 deviceDescription, true, deviceDescription.annotations()));
helenyrwufd296b62016-06-22 17:43:02 -0700503 }
504 }
505
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530506 private void updatePortStatistics(Device device) {
507 if (device.is(PortStatisticsDiscovery.class)) {
508 PortStatisticsDiscovery d = device.as(PortStatisticsDiscovery.class);
Andrea Campanellac3627842017-04-04 18:06:54 +0200509 Collection<PortStatistics> portStatistics = d.discoverPortStatistics();
510 if (portStatistics != null) {
511 providerService.updatePortStatistics(device.id(),
512 portStatistics);
513 }
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530514 } else {
Yuta HIGUCHIe4cb8cf2017-05-01 22:18:28 -0700515 log.debug("No port statistics getter behaviour for device {}",
516 device.id());
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530517 }
518 }
519
Michele Santuari00cc1f72016-09-08 17:05:24 +0200520 private boolean descriptionEquals(Device device, DeviceDescription updatedDeviceDescription) {
Yuta HIGUCHIf381fc72017-01-03 10:39:36 -0800521 return Objects.equal(device.id().uri(), updatedDeviceDescription.deviceUri())
Michele Santuari00cc1f72016-09-08 17:05:24 +0200522 && Objects.equal(device.type(), updatedDeviceDescription.type())
523 && Objects.equal(device.manufacturer(), updatedDeviceDescription.manufacturer())
524 && Objects.equal(device.hwVersion(), updatedDeviceDescription.hwVersion())
525 && Objects.equal(device.swVersion(), updatedDeviceDescription.swVersion())
526 && Objects.equal(device.serialNumber(), updatedDeviceDescription.serialNumber())
527 && Objects.equal(device.chassisId(), updatedDeviceDescription.chassisId())
528 && Objects.equal(device.annotations(), updatedDeviceDescription.annotations());
529 }
530
helenyrwufd296b62016-06-22 17:43:02 -0700531 private void checkAndUpdateDevices() {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200532 Set<DeviceId> deviceSubjects =
533 cfgService.getSubjects(DeviceId.class, NetconfDeviceConfig.class);
534 deviceSubjects.forEach(deviceId -> {
535 NetconfDeviceConfig config =
536 cfgService.getConfig(deviceId, NetconfDeviceConfig.class);
537 DeviceDescription deviceDescription = createDeviceRepresentation(deviceId, config);
538 storeDeviceKey(config.sshKey(), config.username(), config.password(), deviceId);
Andrea Campanella105736e2017-11-23 12:52:43 +0100539 checkAndUpdateDevice(deviceId, deviceDescription, false);
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200540 });
andreaeb70a942015-10-16 21:34:46 -0700541 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530542
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200543 private DeviceDescription createDeviceRepresentation(DeviceId deviceId, NetconfDeviceConfig config) {
544 Preconditions.checkNotNull(deviceId, ISNULL);
545 //Netconf configuration object
546 ChassisId cid = new ChassisId();
547 String ipAddress = config.ip().toString();
548 SparseAnnotations annotations = DefaultAnnotations.builder()
549 .set(IPADDRESS, ipAddress)
550 .set(PORT, String.valueOf(config.port()))
551 .set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase())
Andrea Campanella42e7b862018-09-21 11:56:48 +0200552 .set(AnnotationKeys.PROVIDER_MARK_ONLINE, String.valueOf(true))
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200553 .build();
554 return new DefaultDeviceDescription(
555 deviceId.uri(),
556 Device.Type.SWITCH,
557 UNKNOWN, UNKNOWN,
558 UNKNOWN, UNKNOWN,
559 cid, false,
560 annotations);
561 }
562
563 private void storeDeviceKey(String sshKey, String username, String password, DeviceId deviceId) {
564 if (sshKey.equals("")) {
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600565 deviceKeyAdminService.addKey(
566 DeviceKey.createDeviceKeyUsingUsernamePassword(
567 DeviceKeyId.deviceKeyId(deviceId.toString()),
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200568 null, username, password));
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600569 } else {
570 deviceKeyAdminService.addKey(
571 DeviceKey.createDeviceKeyUsingSshKey(
572 DeviceKeyId.deviceKeyId(deviceId.toString()),
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200573 null, username, password,
574 sshKey));
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600575 }
576 }
577
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700578 private void initiateConnection(DeviceId deviceId, MastershipRole newRole) {
579 try {
580 if (isReachable(deviceId)) {
Andrea Campanella42e7b862018-09-21 11:56:48 +0200581 NetconfDevice device = controller.connectDevice(deviceId);
582 if (device != null) {
583 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.MASTER);
584 } else {
585 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
586 }
587
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700588 }
589 } catch (Exception e) {
590 if (deviceService.getDevice(deviceId) != null) {
591 providerService.deviceDisconnected(deviceId);
592 }
593 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(deviceId.toString()));
Ray Milkey986a47a2018-01-25 11:38:51 -0800594 throw new IllegalStateException(new NetconfException(
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200595 "Can't connect to NETCONF device " + deviceId, e));
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700596
597 }
598 }
599
600 private void discoverPorts(DeviceId deviceId) {
601 Device device = deviceService.getDevice(deviceId);
Andrea Campanella6c71a052016-04-22 11:56:31 -0700602 //TODO remove when PortDiscovery is removed from master
Ray Milkey640fedd2018-02-08 09:02:26 -0800603 if (device.is(DeviceDescriptionDiscovery.class)) {
Andrea Campanella6c71a052016-04-22 11:56:31 -0700604 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
605 device.as(DeviceDescriptionDiscovery.class);
606 providerService.updatePorts(deviceId,
607 deviceDescriptionDiscovery.discoverPortDetails());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700608 } else {
609 log.warn("No portGetter behaviour for device {}", deviceId);
610 }
Gaurav Agrawaldab4d772017-03-29 15:15:13 +0530611
612 // Port statistics discovery
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530613 updatePortStatistics(device);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700614 }
615
616 /**
617 * Return the DeviceId about the device containing the URI.
618 *
Andrea Campanella6c71a052016-04-22 11:56:31 -0700619 * @param ip IP address
Ray Milkeyd4334db2016-04-05 17:39:44 -0700620 * @param port port number
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700621 * @return DeviceId
622 */
623 public DeviceId getDeviceId(String ip, int port) {
624 try {
625 return DeviceId.deviceId(new URI(NETCONF, ip + ":" + port, null));
626 } catch (URISyntaxException e) {
627 throw new IllegalArgumentException("Unable to build deviceID for device "
628 + ip + ":" + port, e);
629 }
630 }
631
Andrea Campanella42e7b862018-09-21 11:56:48 +0200632 private <U> U withDeviceLock(Supplier<U> task, DeviceId deviceId) {
633 final Lock lock = deviceLocks.get(deviceId);
634 lock.lock();
635 try {
636 return task.get();
637 } finally {
638 lock.unlock();
639 }
640 }
641
642 private Runnable withDeviceLock(Runnable task, DeviceId deviceId) {
643 // Wrapper of withDeviceLock(Supplier, ...) for void tasks.
644 return () -> withDeviceLock(() -> {
645 task.run();
646 return null;
647 }, deviceId);
648 }
649
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700650 /**
651 * Listener for configuration events.
652 */
andreaeb70a942015-10-16 21:34:46 -0700653 private class InternalNetworkConfigListener implements NetworkConfigListener {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530654
andreaeb70a942015-10-16 21:34:46 -0700655
656 @Override
657 public void event(NetworkConfigEvent event) {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200658 if (event.configClass().equals(NetconfDeviceConfig.class)) {
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700659 executor.execute(() -> connectDevice((NetconfDeviceConfig) event.config().get()));
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200660 } else {
661 log.warn("Injecting device via this Json is deprecated, " +
662 "please put configuration under devices/ as shown in the wiki");
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200663 }
664
Sanjay Se8dcfee2015-04-23 10:07:08 +0530665 }
666
andreaeb70a942015-10-16 21:34:46 -0700667 @Override
668 public boolean isRelevant(NetworkConfigEvent event) {
Ray Milkey02710432018-02-13 17:08:28 -0800669 return (event.configClass().equals(NetconfDeviceConfig.class)) &&
andreaeb70a942015-10-16 21:34:46 -0700670 (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
671 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530672 }
673 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700674
675 /**
676 * Listener for core device events.
677 */
678 private class InternalDeviceListener implements DeviceListener {
679 @Override
680 public void event(DeviceEvent event) {
Andrea Campanella42e7b862018-09-21 11:56:48 +0200681 DeviceId deviceId = event.subject().id();
682 if (event.type() == DeviceEvent.Type.DEVICE_ADDED && !deviceService.isAvailable(event.subject().id())) {
683 try {
684 checkAndUpdateDevice(deviceId, null, true);
685 } catch (Exception e) {
686 log.error("Unhandled exception checking {}", deviceId, e);
687 }
688 }
689 if (deviceService.isAvailable(event.subject().id())) {
690 executor.execute(() -> discoverPorts(event.subject().id()));
691 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700692 }
693
694 @Override
695 public boolean isRelevant(DeviceEvent event) {
Andrea Campanella42e7b862018-09-21 11:56:48 +0200696 if (event.type() != DeviceEvent.Type.DEVICE_ADDED &&
697 event.type() != DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED) {
Thomas Vachuska5b38dc02018-05-10 15:24:40 -0700698 return false;
699 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700700 if (mastershipService.getMasterFor(event.subject().id()) == null) {
701 return true;
702 }
Andrea Campanella968f93f2017-06-08 11:09:28 +0200703 return (SCHEME_NAME.equalsIgnoreCase(event.subject().annotations().value(AnnotationKeys.PROTOCOL)) ||
704 (SCHEME_NAME.equalsIgnoreCase(event.subject().id().uri().getScheme()))) &&
Michele Santuari576f09c2016-09-28 14:20:00 +0200705 mastershipService.isLocalMaster(event.subject().id());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700706 }
707 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530708}