blob: 9987c6e11d70f7f044ae1868c21cc6b448764848 [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.onlab.packet.ChassisId;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -070023import org.onlab.util.Tools;
24import org.onosproject.cfg.ComponentConfigService;
andreaeb70a942015-10-16 21:34:46 -070025import org.onosproject.core.ApplicationId;
26import org.onosproject.core.CoreService;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070027import org.onosproject.mastership.MastershipService;
Marc De Leenheerb0d131c2016-03-01 20:34:59 -080028import org.onosproject.net.AnnotationKeys;
andreaeb70a942015-10-16 21:34:46 -070029import org.onosproject.net.DefaultAnnotations;
Sanjay Se8dcfee2015-04-23 10:07:08 +053030import org.onosproject.net.Device;
31import org.onosproject.net.DeviceId;
32import org.onosproject.net.MastershipRole;
Ray Milkey02710432018-02-13 17:08:28 -080033import org.onosproject.net.Port;
Saurav Dasa2d37502016-03-25 17:50:40 -070034import org.onosproject.net.PortNumber;
andreaeb70a942015-10-16 21:34:46 -070035import org.onosproject.net.SparseAnnotations;
Ray Milkey02710432018-02-13 17:08:28 -080036import org.onosproject.net.behaviour.PortAdmin;
andreaeb70a942015-10-16 21:34:46 -070037import org.onosproject.net.config.ConfigFactory;
38import org.onosproject.net.config.NetworkConfigEvent;
39import org.onosproject.net.config.NetworkConfigListener;
40import org.onosproject.net.config.NetworkConfigRegistry;
Andrea Campanella34cf65c2017-04-12 13:51:32 +020041import org.onosproject.net.config.basics.SubjectFactories;
Sanjay Se8dcfee2015-04-23 10:07:08 +053042import org.onosproject.net.device.DefaultDeviceDescription;
Andrea Campanella32813682017-10-23 15:29:24 +020043import org.onosproject.net.device.DefaultPortDescription;
Sanjay Se8dcfee2015-04-23 10:07:08 +053044import org.onosproject.net.device.DeviceDescription;
Andrea Campanella6c71a052016-04-22 11:56:31 -070045import org.onosproject.net.device.DeviceDescriptionDiscovery;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070046import org.onosproject.net.device.DeviceEvent;
47import org.onosproject.net.device.DeviceListener;
Sanjay Se8dcfee2015-04-23 10:07:08 +053048import org.onosproject.net.device.DeviceProvider;
49import org.onosproject.net.device.DeviceProviderRegistry;
50import org.onosproject.net.device.DeviceProviderService;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -080051import org.onosproject.net.device.DeviceService;
Andrea Campanellac3627842017-04-04 18:06:54 +020052import org.onosproject.net.device.PortStatistics;
Gaurav Agrawaldab4d772017-03-29 15:15:13 +053053import org.onosproject.net.device.PortStatisticsDiscovery;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070054import org.onosproject.net.key.DeviceKey;
55import org.onosproject.net.key.DeviceKeyAdminService;
56import org.onosproject.net.key.DeviceKeyId;
Sanjay Se8dcfee2015-04-23 10:07:08 +053057import org.onosproject.net.provider.AbstractProvider;
58import org.onosproject.net.provider.ProviderId;
andreaeb70a942015-10-16 21:34:46 -070059import org.onosproject.netconf.NetconfController;
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -070060import org.onosproject.netconf.NetconfDevice;
andreaeb70a942015-10-16 21:34:46 -070061import org.onosproject.netconf.NetconfDeviceListener;
Andrea Campanella8b1cb672016-01-25 13:58:58 -080062import org.onosproject.netconf.NetconfException;
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -070063import org.onosproject.netconf.config.NetconfDeviceConfig;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -070064import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070065import org.osgi.service.component.annotations.Activate;
66import org.osgi.service.component.annotations.Component;
67import org.osgi.service.component.annotations.Deactivate;
68import org.osgi.service.component.annotations.Modified;
69import org.osgi.service.component.annotations.Reference;
70import org.osgi.service.component.annotations.ReferenceCardinality;
Sanjay Se8dcfee2015-04-23 10:07:08 +053071import org.slf4j.Logger;
72
Andrea Campanella087ceb92015-12-07 09:58:34 -080073import java.io.IOException;
Andrea Campanella42e7b862018-09-21 11:56:48 +020074import java.net.InetSocketAddress;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070075import java.net.Socket;
76import java.net.URI;
77import java.net.URISyntaxException;
78import java.util.Arrays;
Andrea Campanellac3627842017-04-04 18:06:54 +020079import java.util.Collection;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -070080import java.util.Dictionary;
mskala832d0472017-06-09 16:31:42 +020081import java.util.Map;
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -070082import java.util.Optional;
Andrea Campanella34cf65c2017-04-12 13:51:32 +020083import java.util.Set;
Andrea Campanella32813682017-10-23 15:29:24 +020084import java.util.concurrent.CompletableFuture;
mskala832d0472017-06-09 16:31:42 +020085import java.util.concurrent.ConcurrentHashMap;
Andrea Campanella5c999e22016-03-01 15:12:53 -080086import java.util.concurrent.ExecutorService;
87import java.util.concurrent.Executors;
helenyrwufd296b62016-06-22 17:43:02 -070088import java.util.concurrent.ScheduledExecutorService;
89import java.util.concurrent.ScheduledFuture;
90import java.util.concurrent.TimeUnit;
mskala832d0472017-06-09 16:31:42 +020091import java.util.concurrent.atomic.AtomicInteger;
Andrea Campanella42e7b862018-09-21 11:56:48 +020092import java.util.concurrent.locks.Lock;
93import java.util.function.Supplier;
andreaeb70a942015-10-16 21:34:46 -070094
Yuta HIGUCHI1624df12016-07-21 16:54:33 -070095import static java.util.concurrent.Executors.newScheduledThreadPool;
Andrea Campanella5c999e22016-03-01 15:12:53 -080096import static org.onlab.util.Tools.groupedThreads;
Thomas Vachuska4167c3f2018-10-16 07:16:31 -070097import static org.onosproject.provider.netconf.device.impl.OsgiPropertyConstants.*;
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 */
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700103@Component(immediate = true,
104 property = {
105 POLL_FREQUENCY_SECONDS_DEFAULT + ":Integer=" + POLL_FREQUENCY_SECONDS_DEFAULT,
106 MAX_RETRIES + ":Integer=" + MAX_RETRIES_DEFAULT,
107 })
Sanjay Se8dcfee2015-04-23 10:07:08 +0530108public class NetconfDeviceProvider extends AbstractProvider
109 implements DeviceProvider {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700110
andreaeb70a942015-10-16 21:34:46 -0700111 private final Logger log = getLogger(getClass());
Sanjay Se8dcfee2015-04-23 10:07:08 +0530112
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700113 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Sanjay Se8dcfee2015-04-23 10:07:08 +0530114 protected DeviceProviderRegistry providerRegistry;
115
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700116 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella101417d2015-12-11 17:58:07 -0800117 protected NetconfController controller;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530118
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700119 @Reference(cardinality = ReferenceCardinality.MANDATORY)
andreaeb70a942015-10-16 21:34:46 -0700120 protected NetworkConfigRegistry cfgService;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530121
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700122 @Reference(cardinality = ReferenceCardinality.MANDATORY)
andreaeb70a942015-10-16 21:34:46 -0700123 protected CoreService coreService;
Thomas Vachuskad6811712015-04-29 21:37:04 -0700124
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700125 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700126 protected DeviceService deviceService;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -0800127
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700128 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700129 protected DeviceKeyAdminService deviceKeyAdminService;
130
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700131 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700132 protected MastershipService mastershipService;
133
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700134 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700135 protected ComponentConfigService componentConfigService;
136
137
Michele Santuari576f09c2016-09-28 14:20:00 +0200138 protected static final String APP_NAME = "org.onosproject.netconf";
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200139 protected static final String SCHEME_NAME = "netconf";
Andrea Campanella101417d2015-12-11 17:58:07 -0800140 private static final String DEVICE_PROVIDER_PACKAGE = "org.onosproject.netconf.provider.device";
141 private static final String UNKNOWN = "unknown";
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700142 protected static final String ISNULL = "NetconfDeviceInfo is null";
143 private static final String IPADDRESS = "ipaddress";
144 private static final String NETCONF = "netconf";
145 private static final String PORT = "port";
helenyrwufd296b62016-06-22 17:43:02 -0700146 private static final int CORE_POOL_SIZE = 10;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700147
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700148 //@Property(name = "pollFrequency", intValue = DEFAULT_POLL_FREQUENCY_SECONDS,
149 // label = "Configure poll frequency for port status and statistics; " +
150 // "default is 30 sec")
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700151 private int pollFrequency = POLL_FREQUENCY_SECONDS_DEFAULT;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530152
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700153 //@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")
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700156 private int maxRetries = MAX_RETRIES_DEFAULT;
mskala832d0472017-06-09 16:31:42 +0200157
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
Ray Milkey02710432018-02-13 17:08:28 -0800172 protected final ConfigFactory factory =
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 }
Ray Milkey02710432018-02-13 17:08:28 -0800181 };
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200182
Michele Santuari576f09c2016-09-28 14:20:00 +0200183 protected final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
andreaeb70a942015-10-16 21:34:46 -0700184 private ApplicationId appId;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700185 private boolean active;
Andrea Campanella42e7b862018-09-21 11:56:48 +0200186 private final Striped<Lock> deviceLocks = Striped.lock(30);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530187
Sanjay Se8dcfee2015-04-23 10:07:08 +0530188
189 @Activate
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700190 public void activate(ComponentContext context) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700191 active = true;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700192 componentConfigService.registerProperties(getClass());
Sanjay Se8dcfee2015-04-23 10:07:08 +0530193 providerService = providerRegistry.register(this);
Andrea Campanella101417d2015-12-11 17:58:07 -0800194 appId = coreService.registerApplication(APP_NAME);
Ray Milkey02710432018-02-13 17:08:28 -0800195 cfgService.registerConfigFactory(factory);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700196 cfgService.addListener(cfgListener);
andreaeb70a942015-10-16 21:34:46 -0700197 controller.addDeviceListener(innerNodeListener);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700198 deviceService.addListener(deviceListener);
Andrea Campanella7d8449b2016-03-02 10:16:42 -0800199 executor.execute(NetconfDeviceProvider.this::connectDevices);
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700200 modified(context);
Thomas Vachuskad6811712015-04-29 21:37:04 -0700201 log.info("Started");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530202 }
203
andreaeb70a942015-10-16 21:34:46 -0700204
Sanjay Se8dcfee2015-04-23 10:07:08 +0530205 @Deactivate
andreaeb70a942015-10-16 21:34:46 -0700206 public void deactivate() {
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700207 componentConfigService.unregisterProperties(getClass(), false);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700208 deviceService.removeListener(deviceListener);
209 active = false;
210 controller.getNetconfDevices().forEach(id -> {
211 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(id.toString()));
212 controller.disconnectDevice(id, true);
213 });
Andrea Campanella86294db2016-03-07 11:42:49 -0800214 controller.removeDeviceListener(innerNodeListener);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700215 deviceService.removeListener(deviceListener);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530216 providerRegistry.unregister(this);
217 providerService = null;
mskala832d0472017-06-09 16:31:42 +0200218 retriedPortDiscoveryMap.clear();
Ray Milkey02710432018-02-13 17:08:28 -0800219 cfgService.unregisterConfigFactory(factory);
helenyrwufd296b62016-06-22 17:43:02 -0700220 scheduledTask.cancel(true);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700221 executor.shutdown();
Sanjay Seb5eebb2015-04-24 15:44:50 +0530222 log.info("Stopped");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530223 }
224
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700225
226 @Modified
227 public void modified(ComponentContext context) {
228 if (context != null) {
229 Dictionary<?, ?> properties = context.getProperties();
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700230 pollFrequency = Tools.getIntegerProperty(properties, POLL_FREQUENCY_SECONDS,
231 POLL_FREQUENCY_SECONDS_DEFAULT);
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700232 log.info("Configured. Poll frequency is configured to {} seconds", pollFrequency);
mskala832d0472017-06-09 16:31:42 +0200233
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700234 maxRetries = Tools.getIntegerProperty(properties, MAX_RETRIES, MAX_RETRIES_DEFAULT);
mskala832d0472017-06-09 16:31:42 +0200235 log.info("Configured. Number of retries is configured to {} times", maxRetries);
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700236 }
237 if (scheduledTask != null) {
238 scheduledTask.cancel(false);
239 }
240 scheduledTask = schedulePolling();
241 }
242
andreaeb70a942015-10-16 21:34:46 -0700243 public NetconfDeviceProvider() {
Andrea Campanella101417d2015-12-11 17:58:07 -0800244 super(new ProviderId(SCHEME_NAME, DEVICE_PROVIDER_PACKAGE));
Sanjay Se8dcfee2015-04-23 10:07:08 +0530245 }
246
helenyrwufd296b62016-06-22 17:43:02 -0700247 // Checks connection to devices in the config file
248 // every DEFAULT_POLL_FREQUENCY_SECONDS seconds.
249 private ScheduledFuture schedulePolling() {
Yuta HIGUCHIc2b82e32017-03-10 14:33:41 -0800250 return connectionExecutor.scheduleAtFixedRate(exceptionSafe(this::checkAndUpdateDevices),
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700251 pollFrequency / 10,
252 pollFrequency, TimeUnit.SECONDS);
helenyrwufd296b62016-06-22 17:43:02 -0700253 }
254
Yuta HIGUCHIc2b82e32017-03-10 14:33:41 -0800255 private Runnable exceptionSafe(Runnable runnable) {
256 return new Runnable() {
257
258 @Override
259 public void run() {
260 try {
261 runnable.run();
262 } catch (Exception e) {
263 log.error("Unhandled Exception", e);
264 }
265 }
266 };
267 }
268
Sanjay Se8dcfee2015-04-23 10:07:08 +0530269 @Override
270 public void triggerProbe(DeviceId deviceId) {
andreaeb70a942015-10-16 21:34:46 -0700271 // TODO: This will be implemented later.
Yuta HIGUCHIe4cb8cf2017-05-01 22:18:28 -0700272 log.debug("Should be triggering probe on device {}", deviceId);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530273 }
274
275 @Override
276 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700277 if (active) {
278 switch (newRole) {
279 case MASTER:
Andrea Campanella42e7b862018-09-21 11:56:48 +0200280 withDeviceLock(
281 () -> initiateConnection(deviceId, newRole), deviceId).run();
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700282 log.debug("Accepting mastership role change to {} for device {}", newRole, deviceId);
283 break;
284 case STANDBY:
285 controller.disconnectDevice(deviceId, false);
286 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.STANDBY);
287 //else no-op
288 break;
289 case NONE:
290 controller.disconnectDevice(deviceId, false);
291 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
292 break;
293 default:
294 log.error("Unimplemented Mastership state : {}", newRole);
295
296 }
297 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530298 }
299
300 @Override
301 public boolean isReachable(DeviceId deviceId) {
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700302
303 boolean sessionExists =
304 Optional.ofNullable(controller.getDevicesMap().get(deviceId))
305 .map(NetconfDevice::isActive)
306 .orElse(false);
307 if (sessionExists) {
308 return true;
309 }
310
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700311 //FIXME this is a workaround util device state is shared
312 // between controller instances.
313 Device device = deviceService.getDevice(deviceId);
314 String ip;
315 int port;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700316 if (device != null) {
317 ip = device.annotations().value(IPADDRESS);
318 port = Integer.parseInt(device.annotations().value(PORT));
319 } else {
320 String[] info = deviceId.toString().split(":");
321 if (info.length == 3) {
322 ip = info[1];
323 port = Integer.parseInt(info[2]);
324 } else {
325 ip = Arrays.asList(info).stream().filter(el -> !el.equals(info[0])
326 && !el.equals(info[info.length - 1]))
327 .reduce((t, u) -> t + ":" + u)
328 .get();
329 log.debug("ip v6 {}", ip);
330 port = Integer.parseInt(info[info.length - 1]);
331 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530332 }
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700333 // FIXME just opening TCP session probably is not the appropriate
334 // method to test reachability.
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700335 //test connection to device opening a socket to it.
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700336 log.debug("Testing reachability for {}:{}", ip, port);
Andrea Campanella42e7b862018-09-21 11:56:48 +0200337 Socket socket = new Socket();
338 try {
339 socket.connect(new InetSocketAddress(ip, port), 1000);
Yuta HIGUCHI0454d702017-03-17 10:08:38 -0700340 log.debug("rechability of {}, {}, {}", deviceId, socket.isConnected(), !socket.isClosed());
Andrea Campanella42e7b862018-09-21 11:56:48 +0200341 boolean isConnected = socket.isConnected() && !socket.isClosed();
342 socket.close();
343 return isConnected;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700344 } catch (IOException e) {
345 log.info("Device {} is not reachable", deviceId);
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700346 log.debug(" error details", e);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700347 return false;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700348 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530349 }
350
Saurav Dasa2d37502016-03-25 17:50:40 -0700351 @Override
352 public void changePortState(DeviceId deviceId, PortNumber portNumber,
353 boolean enable) {
Andrea Campanella32813682017-10-23 15:29:24 +0200354 Device device = deviceService.getDevice(deviceId);
355 if (mastershipService.isLocalMaster(deviceId)) {
356 if (device.is(PortAdmin.class)) {
357 PortAdmin portAdmin =
358 device.as(PortAdmin.class);
359 CompletableFuture<Boolean> modified;
360 if (enable) {
361 modified = portAdmin.enable(portNumber);
362 } else {
363 modified = portAdmin.disable(portNumber);
364 }
365 modified.thenAccept(result -> {
366 if (result) {
367 Port port = deviceService.getPort(deviceId, portNumber);
368 //rebuilding port description with admin state changed.
369 providerService.portStatusChanged(deviceId,
Yuta HIGUCHI53e47962018-03-01 23:50:48 -0800370 DefaultPortDescription.builder()
371 .withPortNumber(portNumber)
372 .isEnabled(enable)
373 .isRemoved(false)
374 .type(port.type())
375 .portSpeed(port.portSpeed())
376 .annotations((SparseAnnotations) port.annotations())
377 .build());
Andrea Campanella32813682017-10-23 15:29:24 +0200378 } 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
Thomas Vachuska5b38dc02018-05-10 15:24:40 -0700391 @Override
392 public void triggerDisconnect(DeviceId deviceId) {
393 log.debug("Forcing disconnect for device {}", deviceId);
394 controller.disconnectDevice(deviceId, true);
395 }
396
andreaeb70a942015-10-16 21:34:46 -0700397 private class InnerNetconfDeviceListener implements NetconfDeviceListener {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530398
Andrea Campanella101417d2015-12-11 17:58:07 -0800399
andreaeb70a942015-10-16 21:34:46 -0700400 @Override
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700401 public void deviceAdded(DeviceId deviceId) {
402 //no-op
403 log.debug("Netconf device {} added to Netconf subController", deviceId);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530404 }
405
406 @Override
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700407 public void deviceRemoved(DeviceId deviceId) {
408 Preconditions.checkNotNull(deviceId, ISNULL);
helenyrwufd296b62016-06-22 17:43:02 -0700409
410 if (deviceService.getDevice(deviceId) != null) {
411 providerService.deviceDisconnected(deviceId);
mskala832d0472017-06-09 16:31:42 +0200412 retriedPortDiscoveryMap.remove(deviceId);
helenyrwufd296b62016-06-22 17:43:02 -0700413 log.debug("Netconf device {} removed from Netconf subController", deviceId);
414 } else {
415 log.warn("Netconf device {} does not exist in the store, " +
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530416 "it may already have been removed", deviceId);
helenyrwufd296b62016-06-22 17:43:02 -0700417 }
andreaeb70a942015-10-16 21:34:46 -0700418 }
419 }
420
andreaeb70a942015-10-16 21:34:46 -0700421 private void connectDevices() {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200422 Set<DeviceId> deviceSubjects =
423 cfgService.getSubjects(DeviceId.class, NetconfDeviceConfig.class);
424 deviceSubjects.forEach(deviceId -> {
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700425 connectDevice(cfgService.getConfig(deviceId, NetconfDeviceConfig.class));
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200426 });
helenyrwufd296b62016-06-22 17:43:02 -0700427 }
428
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700429
430 private void connectDevice(NetconfDeviceConfig config) {
431 if (config == null) {
432 return;
433 }
434 DeviceId deviceId = config.subject();
435 if (!deviceId.uri().getScheme().equals(SCHEME_NAME)) {
436 // not under my scheme, skipping
437 log.trace("{} not my scheme, skipping", deviceId);
438 return;
439 }
Andrea Campanella42e7b862018-09-21 11:56:48 +0200440 if (!isReachable(deviceId)) {
441 log.warn("Can't connect to device {}", deviceId);
442 return;
443 }
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700444 DeviceDescription deviceDescription = createDeviceRepresentation(deviceId, config);
445 log.debug("Connecting NETCONF device {}, on {}:{} with username {}",
446 deviceId, config.ip(), config.port(), config.username());
447 storeDeviceKey(config.sshKey(), config.username(), config.password(), deviceId);
Andrea Campanella105736e2017-11-23 12:52:43 +0100448 retriedPortDiscoveryMap.put(deviceId, new AtomicInteger(0));
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700449 if (deviceService.getDevice(deviceId) == null) {
450 providerService.deviceConnected(deviceId, deviceDescription);
451 }
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700452 }
453
Andrea Campanella105736e2017-11-23 12:52:43 +0100454 private void checkAndUpdateDevice(DeviceId deviceId, DeviceDescription deviceDescription, boolean newlyConnected) {
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530455 Device device = deviceService.getDevice(deviceId);
456 if (device == null) {
Andrea Campanella105736e2017-11-23 12:52:43 +0100457 log.debug("Device {} has not been added to store, since it's not reachable", deviceId);
458 return;
459 }
460 boolean isReachable = isReachable(deviceId);
461 if (!isReachable && deviceService.isAvailable(deviceId)) {
462 providerService.deviceDisconnected(deviceId);
463 return;
Andrea Campanella42e7b862018-09-21 11:56:48 +0200464 } else if (newlyConnected && mastershipService.isLocalMaster(deviceId)) {
Andrea Campanella105736e2017-11-23 12:52:43 +0100465 updateDeviceDescription(deviceId, deviceDescription, device);
466 }
467 if (isReachable && deviceService.isAvailable(deviceId) &&
468 mastershipService.isLocalMaster(deviceId)) {
469 //if ports are not discovered, retry the discovery
470 if (deviceService.getPorts(deviceId).isEmpty() &&
471 retriedPortDiscoveryMap.get(deviceId).getAndIncrement() < maxRetries) {
472 discoverPorts(deviceId);
473 }
474 updatePortStatistics(device);
475 }
476 }
477
478 private void updateDeviceDescription(DeviceId deviceId, DeviceDescription deviceDescription, Device device) {
479 if (device.is(DeviceDescriptionDiscovery.class)) {
480 if (mastershipService.isLocalMaster(deviceId)) {
481 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
482 device.as(DeviceDescriptionDiscovery.class);
483 DeviceDescription updatedDeviceDescription =
484 deviceDescriptionDiscovery.discoverDeviceDetails();
485 if (updatedDeviceDescription != null &&
486 !descriptionEquals(device, updatedDeviceDescription)) {
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530487 providerService.deviceConnected(
488 deviceId, new DefaultDeviceDescription(
Andrea Campanella105736e2017-11-23 12:52:43 +0100489 updatedDeviceDescription, true,
490 updatedDeviceDescription.annotations()));
Ray Milkey33306ba2018-09-20 13:27:25 -0700491 } else if (updatedDeviceDescription == null) {
Andrea Campanella105736e2017-11-23 12:52:43 +0100492 providerService.deviceConnected(
493 deviceId, new DefaultDeviceDescription(
494 deviceDescription, true,
495 deviceDescription.annotations()));
Konstantinos Kanonakis4d67dd82016-08-05 12:18:52 -0500496 }
helenyrwufd296b62016-06-22 17:43:02 -0700497 }
Andrea Campanella105736e2017-11-23 12:52:43 +0100498 } else {
499 log.warn("No DeviceDescriptionDiscovery behaviour for device {} " +
500 "using DefaultDeviceDescription", deviceId);
501 providerService.deviceConnected(
502 deviceId, new DefaultDeviceDescription(
503 deviceDescription, true, deviceDescription.annotations()));
helenyrwufd296b62016-06-22 17:43:02 -0700504 }
505 }
506
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530507 private void updatePortStatistics(Device device) {
508 if (device.is(PortStatisticsDiscovery.class)) {
509 PortStatisticsDiscovery d = device.as(PortStatisticsDiscovery.class);
Andrea Campanellac3627842017-04-04 18:06:54 +0200510 Collection<PortStatistics> portStatistics = d.discoverPortStatistics();
511 if (portStatistics != null) {
512 providerService.updatePortStatistics(device.id(),
513 portStatistics);
514 }
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530515 } else {
Yuta HIGUCHIe4cb8cf2017-05-01 22:18:28 -0700516 log.debug("No port statistics getter behaviour for device {}",
517 device.id());
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530518 }
519 }
520
Michele Santuari00cc1f72016-09-08 17:05:24 +0200521 private boolean descriptionEquals(Device device, DeviceDescription updatedDeviceDescription) {
Yuta HIGUCHIf381fc72017-01-03 10:39:36 -0800522 return Objects.equal(device.id().uri(), updatedDeviceDescription.deviceUri())
Michele Santuari00cc1f72016-09-08 17:05:24 +0200523 && Objects.equal(device.type(), updatedDeviceDescription.type())
524 && Objects.equal(device.manufacturer(), updatedDeviceDescription.manufacturer())
525 && Objects.equal(device.hwVersion(), updatedDeviceDescription.hwVersion())
526 && Objects.equal(device.swVersion(), updatedDeviceDescription.swVersion())
527 && Objects.equal(device.serialNumber(), updatedDeviceDescription.serialNumber())
528 && Objects.equal(device.chassisId(), updatedDeviceDescription.chassisId())
529 && Objects.equal(device.annotations(), updatedDeviceDescription.annotations());
530 }
531
helenyrwufd296b62016-06-22 17:43:02 -0700532 private void checkAndUpdateDevices() {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200533 Set<DeviceId> deviceSubjects =
534 cfgService.getSubjects(DeviceId.class, NetconfDeviceConfig.class);
535 deviceSubjects.forEach(deviceId -> {
536 NetconfDeviceConfig config =
537 cfgService.getConfig(deviceId, NetconfDeviceConfig.class);
538 DeviceDescription deviceDescription = createDeviceRepresentation(deviceId, config);
539 storeDeviceKey(config.sshKey(), config.username(), config.password(), deviceId);
Andrea Campanella105736e2017-11-23 12:52:43 +0100540 checkAndUpdateDevice(deviceId, deviceDescription, false);
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200541 });
andreaeb70a942015-10-16 21:34:46 -0700542 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530543
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200544 private DeviceDescription createDeviceRepresentation(DeviceId deviceId, NetconfDeviceConfig config) {
545 Preconditions.checkNotNull(deviceId, ISNULL);
546 //Netconf configuration object
547 ChassisId cid = new ChassisId();
548 String ipAddress = config.ip().toString();
549 SparseAnnotations annotations = DefaultAnnotations.builder()
550 .set(IPADDRESS, ipAddress)
551 .set(PORT, String.valueOf(config.port()))
552 .set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase())
Andrea Campanella42e7b862018-09-21 11:56:48 +0200553 .set(AnnotationKeys.PROVIDER_MARK_ONLINE, String.valueOf(true))
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200554 .build();
555 return new DefaultDeviceDescription(
556 deviceId.uri(),
557 Device.Type.SWITCH,
558 UNKNOWN, UNKNOWN,
559 UNKNOWN, UNKNOWN,
560 cid, false,
561 annotations);
562 }
563
564 private void storeDeviceKey(String sshKey, String username, String password, DeviceId deviceId) {
565 if (sshKey.equals("")) {
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600566 deviceKeyAdminService.addKey(
567 DeviceKey.createDeviceKeyUsingUsernamePassword(
568 DeviceKeyId.deviceKeyId(deviceId.toString()),
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200569 null, username, password));
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600570 } else {
571 deviceKeyAdminService.addKey(
572 DeviceKey.createDeviceKeyUsingSshKey(
573 DeviceKeyId.deviceKeyId(deviceId.toString()),
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200574 null, username, password,
575 sshKey));
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600576 }
577 }
578
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700579 private void initiateConnection(DeviceId deviceId, MastershipRole newRole) {
580 try {
581 if (isReachable(deviceId)) {
Andrea Campanella42e7b862018-09-21 11:56:48 +0200582 NetconfDevice device = controller.connectDevice(deviceId);
583 if (device != null) {
584 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.MASTER);
585 } else {
586 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
587 }
588
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700589 }
590 } catch (Exception e) {
591 if (deviceService.getDevice(deviceId) != null) {
592 providerService.deviceDisconnected(deviceId);
593 }
594 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(deviceId.toString()));
Ray Milkey986a47a2018-01-25 11:38:51 -0800595 throw new IllegalStateException(new NetconfException(
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200596 "Can't connect to NETCONF device " + deviceId, e));
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700597
598 }
599 }
600
601 private void discoverPorts(DeviceId deviceId) {
602 Device device = deviceService.getDevice(deviceId);
Andrea Campanella6c71a052016-04-22 11:56:31 -0700603 //TODO remove when PortDiscovery is removed from master
Ray Milkey640fedd2018-02-08 09:02:26 -0800604 if (device.is(DeviceDescriptionDiscovery.class)) {
Andrea Campanella6c71a052016-04-22 11:56:31 -0700605 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
606 device.as(DeviceDescriptionDiscovery.class);
607 providerService.updatePorts(deviceId,
608 deviceDescriptionDiscovery.discoverPortDetails());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700609 } else {
610 log.warn("No portGetter behaviour for device {}", deviceId);
611 }
Gaurav Agrawaldab4d772017-03-29 15:15:13 +0530612
613 // Port statistics discovery
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530614 updatePortStatistics(device);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700615 }
616
617 /**
618 * Return the DeviceId about the device containing the URI.
619 *
Andrea Campanella6c71a052016-04-22 11:56:31 -0700620 * @param ip IP address
Ray Milkeyd4334db2016-04-05 17:39:44 -0700621 * @param port port number
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700622 * @return DeviceId
623 */
624 public DeviceId getDeviceId(String ip, int port) {
625 try {
626 return DeviceId.deviceId(new URI(NETCONF, ip + ":" + port, null));
627 } catch (URISyntaxException e) {
628 throw new IllegalArgumentException("Unable to build deviceID for device "
629 + ip + ":" + port, e);
630 }
631 }
632
Andrea Campanella42e7b862018-09-21 11:56:48 +0200633 private <U> U withDeviceLock(Supplier<U> task, DeviceId deviceId) {
634 final Lock lock = deviceLocks.get(deviceId);
635 lock.lock();
636 try {
637 return task.get();
638 } finally {
639 lock.unlock();
640 }
641 }
642
643 private Runnable withDeviceLock(Runnable task, DeviceId deviceId) {
644 // Wrapper of withDeviceLock(Supplier, ...) for void tasks.
645 return () -> withDeviceLock(() -> {
646 task.run();
647 return null;
648 }, deviceId);
649 }
650
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700651 /**
652 * Listener for configuration events.
653 */
andreaeb70a942015-10-16 21:34:46 -0700654 private class InternalNetworkConfigListener implements NetworkConfigListener {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530655
andreaeb70a942015-10-16 21:34:46 -0700656
657 @Override
658 public void event(NetworkConfigEvent event) {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200659 if (event.configClass().equals(NetconfDeviceConfig.class)) {
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700660 executor.execute(() -> connectDevice((NetconfDeviceConfig) event.config().get()));
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200661 } else {
662 log.warn("Injecting device via this Json is deprecated, " +
663 "please put configuration under devices/ as shown in the wiki");
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200664 }
665
Sanjay Se8dcfee2015-04-23 10:07:08 +0530666 }
667
andreaeb70a942015-10-16 21:34:46 -0700668 @Override
669 public boolean isRelevant(NetworkConfigEvent event) {
Ray Milkey02710432018-02-13 17:08:28 -0800670 return (event.configClass().equals(NetconfDeviceConfig.class)) &&
andreaeb70a942015-10-16 21:34:46 -0700671 (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
672 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530673 }
674 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700675
676 /**
677 * Listener for core device events.
678 */
679 private class InternalDeviceListener implements DeviceListener {
680 @Override
681 public void event(DeviceEvent event) {
Andrea Campanella36f1df92018-10-01 14:54:01 +0200682 Device device = event.subject();
683 DeviceId deviceId = device.id();
Andrea Campanella42e7b862018-09-21 11:56:48 +0200684 if (event.type() == DeviceEvent.Type.DEVICE_ADDED && !deviceService.isAvailable(event.subject().id())) {
685 try {
Andrea Campanella36f1df92018-10-01 14:54:01 +0200686 DeviceDescription description = new DefaultDeviceDescription(deviceId.uri(), device.type(),
687 device.manufacturer(), device.hwVersion(), device.swVersion(),
688 device.serialNumber(), device.chassisId(),
689 (SparseAnnotations) device.annotations());
690 checkAndUpdateDevice(deviceId, description, true);
Andrea Campanella42e7b862018-09-21 11:56:48 +0200691 } catch (Exception e) {
692 log.error("Unhandled exception checking {}", deviceId, e);
693 }
694 }
695 if (deviceService.isAvailable(event.subject().id())) {
696 executor.execute(() -> discoverPorts(event.subject().id()));
697 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700698 }
699
700 @Override
701 public boolean isRelevant(DeviceEvent event) {
Andrea Campanella42e7b862018-09-21 11:56:48 +0200702 if (event.type() != DeviceEvent.Type.DEVICE_ADDED &&
703 event.type() != DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED) {
Thomas Vachuska5b38dc02018-05-10 15:24:40 -0700704 return false;
705 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700706 if (mastershipService.getMasterFor(event.subject().id()) == null) {
707 return true;
708 }
Andrea Campanella968f93f2017-06-08 11:09:28 +0200709 return (SCHEME_NAME.equalsIgnoreCase(event.subject().annotations().value(AnnotationKeys.PROTOCOL)) ||
710 (SCHEME_NAME.equalsIgnoreCase(event.subject().id().uri().getScheme()))) &&
Michele Santuari576f09c2016-09-28 14:20:00 +0200711 mastershipService.isLocalMaster(event.subject().id());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700712 }
713 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530714}