blob: 98d135f396b432e300bf79bda6157004527cc67c [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
andreaeb70a942015-10-16 21:34:46 -070019import com.google.common.base.Preconditions;
David K. Bainbridge56e90232018-12-18 23:25:08 -080020import org.apache.commons.lang3.tuple.Triple;
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +010021import 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.CoreService;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070026import org.onosproject.mastership.MastershipService;
Marc De Leenheerb0d131c2016-03-01 20:34:59 -080027import org.onosproject.net.AnnotationKeys;
andreaeb70a942015-10-16 21:34:46 -070028import org.onosproject.net.DefaultAnnotations;
Sanjay Se8dcfee2015-04-23 10:07:08 +053029import org.onosproject.net.Device;
30import org.onosproject.net.DeviceId;
31import org.onosproject.net.MastershipRole;
Ray Milkey02710432018-02-13 17:08:28 -080032import org.onosproject.net.Port;
Saurav Dasa2d37502016-03-25 17:50:40 -070033import org.onosproject.net.PortNumber;
andreaeb70a942015-10-16 21:34:46 -070034import org.onosproject.net.SparseAnnotations;
Ray Milkey02710432018-02-13 17:08:28 -080035import org.onosproject.net.behaviour.PortAdmin;
andreaeb70a942015-10-16 21:34:46 -070036import org.onosproject.net.config.ConfigFactory;
37import org.onosproject.net.config.NetworkConfigEvent;
38import org.onosproject.net.config.NetworkConfigListener;
39import org.onosproject.net.config.NetworkConfigRegistry;
Andrea Campanella34cf65c2017-04-12 13:51:32 +020040import org.onosproject.net.config.basics.SubjectFactories;
Sanjay Se8dcfee2015-04-23 10:07:08 +053041import org.onosproject.net.device.DefaultDeviceDescription;
Andrea Campanella32813682017-10-23 15:29:24 +020042import org.onosproject.net.device.DefaultPortDescription;
Sanjay Se8dcfee2015-04-23 10:07:08 +053043import org.onosproject.net.device.DeviceDescription;
Andrea Campanella6c71a052016-04-22 11:56:31 -070044import org.onosproject.net.device.DeviceDescriptionDiscovery;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070045import org.onosproject.net.device.DeviceEvent;
46import org.onosproject.net.device.DeviceListener;
Sanjay Se8dcfee2015-04-23 10:07:08 +053047import org.onosproject.net.device.DeviceProvider;
48import org.onosproject.net.device.DeviceProviderRegistry;
49import org.onosproject.net.device.DeviceProviderService;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -080050import org.onosproject.net.device.DeviceService;
Andrea Campanellac3627842017-04-04 18:06:54 +020051import org.onosproject.net.device.PortStatistics;
Gaurav Agrawaldab4d772017-03-29 15:15:13 +053052import org.onosproject.net.device.PortStatisticsDiscovery;
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +010053import org.onosproject.net.driver.DefaultDriverData;
54import org.onosproject.net.driver.DefaultDriverHandler;
55import org.onosproject.net.driver.Driver;
56import org.onosproject.net.driver.DriverData;
57import org.onosproject.net.driver.DriverHandler;
58import org.onosproject.net.driver.DriverService;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070059import org.onosproject.net.key.DeviceKey;
60import org.onosproject.net.key.DeviceKeyAdminService;
61import org.onosproject.net.key.DeviceKeyId;
Sanjay Se8dcfee2015-04-23 10:07:08 +053062import org.onosproject.net.provider.AbstractProvider;
63import org.onosproject.net.provider.ProviderId;
andreaeb70a942015-10-16 21:34:46 -070064import org.onosproject.netconf.NetconfController;
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -070065import org.onosproject.netconf.NetconfDevice;
andreaeb70a942015-10-16 21:34:46 -070066import org.onosproject.netconf.NetconfDeviceListener;
Andrea Campanella8b1cb672016-01-25 13:58:58 -080067import org.onosproject.netconf.NetconfException;
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -070068import org.onosproject.netconf.config.NetconfDeviceConfig;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -070069import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070070import org.osgi.service.component.annotations.Activate;
71import org.osgi.service.component.annotations.Component;
72import org.osgi.service.component.annotations.Deactivate;
73import org.osgi.service.component.annotations.Modified;
74import org.osgi.service.component.annotations.Reference;
75import org.osgi.service.component.annotations.ReferenceCardinality;
Sanjay Se8dcfee2015-04-23 10:07:08 +053076import org.slf4j.Logger;
77
Andrea Campanella087ceb92015-12-07 09:58:34 -080078import java.io.IOException;
Andrea Campanella42e7b862018-09-21 11:56:48 +020079import java.net.InetSocketAddress;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070080import java.net.Socket;
Andrea Campanellac3627842017-04-04 18:06:54 +020081import java.util.Collection;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -070082import java.util.Dictionary;
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;
Andrea Campanella32813682017-10-23 15:29:24 +020086import java.util.concurrent.CompletableFuture;
mskala832d0472017-06-09 16:31:42 +020087import java.util.concurrent.ConcurrentHashMap;
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +010088import java.util.concurrent.ExecutionException;
Andrea Campanella5c999e22016-03-01 15:12:53 -080089import java.util.concurrent.ExecutorService;
90import java.util.concurrent.Executors;
helenyrwufd296b62016-06-22 17:43:02 -070091import java.util.concurrent.ScheduledExecutorService;
92import java.util.concurrent.ScheduledFuture;
93import java.util.concurrent.TimeUnit;
mskala832d0472017-06-09 16:31:42 +020094import java.util.concurrent.atomic.AtomicInteger;
Andrea Campanella42e7b862018-09-21 11:56:48 +020095import java.util.concurrent.locks.Lock;
96import java.util.function.Supplier;
andreaeb70a942015-10-16 21:34:46 -070097
Yuta HIGUCHI1624df12016-07-21 16:54:33 -070098import static java.util.concurrent.Executors.newScheduledThreadPool;
Andrea Campanella5c999e22016-03-01 15:12:53 -080099import static org.onlab.util.Tools.groupedThreads;
David K. Bainbridge56e90232018-12-18 23:25:08 -0800100import static org.onosproject.netconf.NetconfDeviceInfo.extractIpPortPath;
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100101import static org.onosproject.provider.netconf.device.impl.OsgiPropertyConstants.MAX_RETRIES;
102import static org.onosproject.provider.netconf.device.impl.OsgiPropertyConstants.MAX_RETRIES_DEFAULT;
103import static org.onosproject.provider.netconf.device.impl.OsgiPropertyConstants.POLL_FREQUENCY_SECONDS;
104import static org.onosproject.provider.netconf.device.impl.OsgiPropertyConstants.POLL_FREQUENCY_SECONDS_DEFAULT;
andreaeb70a942015-10-16 21:34:46 -0700105import static org.slf4j.LoggerFactory.getLogger;
106
Sanjay Se8dcfee2015-04-23 10:07:08 +0530107/**
andreaeb70a942015-10-16 21:34:46 -0700108 * Provider which uses an NETCONF controller to detect device.
Sanjay Se8dcfee2015-04-23 10:07:08 +0530109 */
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700110@Component(immediate = true,
111 property = {
112 POLL_FREQUENCY_SECONDS_DEFAULT + ":Integer=" + POLL_FREQUENCY_SECONDS_DEFAULT,
113 MAX_RETRIES + ":Integer=" + MAX_RETRIES_DEFAULT,
114 })
Sanjay Se8dcfee2015-04-23 10:07:08 +0530115public class NetconfDeviceProvider extends AbstractProvider
116 implements DeviceProvider {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700117
andreaeb70a942015-10-16 21:34:46 -0700118 private final Logger log = getLogger(getClass());
Sanjay Se8dcfee2015-04-23 10:07:08 +0530119
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700120 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Sanjay Se8dcfee2015-04-23 10:07:08 +0530121 protected DeviceProviderRegistry providerRegistry;
122
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700123 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella101417d2015-12-11 17:58:07 -0800124 protected NetconfController controller;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530125
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700126 @Reference(cardinality = ReferenceCardinality.MANDATORY)
andreaeb70a942015-10-16 21:34:46 -0700127 protected NetworkConfigRegistry cfgService;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530128
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700129 @Reference(cardinality = ReferenceCardinality.MANDATORY)
andreaeb70a942015-10-16 21:34:46 -0700130 protected CoreService coreService;
Thomas Vachuskad6811712015-04-29 21:37:04 -0700131
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700132 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700133 protected DeviceService deviceService;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -0800134
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700135 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100136 protected DriverService driverService;
137
138 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700139 protected DeviceKeyAdminService deviceKeyAdminService;
140
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700141 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700142 protected MastershipService mastershipService;
143
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700144 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700145 protected ComponentConfigService componentConfigService;
146
147
Michele Santuari576f09c2016-09-28 14:20:00 +0200148 protected static final String APP_NAME = "org.onosproject.netconf";
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200149 protected static final String SCHEME_NAME = "netconf";
Andrea Campanella101417d2015-12-11 17:58:07 -0800150 private static final String DEVICE_PROVIDER_PACKAGE = "org.onosproject.netconf.provider.device";
151 private static final String UNKNOWN = "unknown";
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700152 protected static final String ISNULL = "NetconfDeviceInfo is null";
153 private static final String IPADDRESS = "ipaddress";
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700154 private static final String PORT = "port";
David K. Bainbridge56e90232018-12-18 23:25:08 -0800155 private static final String PATH = "path";
helenyrwufd296b62016-06-22 17:43:02 -0700156 private static final int CORE_POOL_SIZE = 10;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700157
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100158 /**
159 * Configure poll frequency for port status and statistics; default is 30 sec.
160 */
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700161 private int pollFrequency = POLL_FREQUENCY_SECONDS_DEFAULT;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530162
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100163 /**
164 * Configure maximum allowed number of retries for obtaining list of ports; default is 5 times.
165 */
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700166 private int maxRetries = MAX_RETRIES_DEFAULT;
mskala832d0472017-06-09 16:31:42 +0200167
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100168 protected ExecutorService connectionExecutor = Executors.newFixedThreadPool(CORE_POOL_SIZE,
169 groupedThreads("onos/netconfDeviceProviderConnection",
170 "connection-executor-%d", log));
171 protected ScheduledExecutorService pollingExecutor = newScheduledThreadPool(CORE_POOL_SIZE,
172 groupedThreads("onos/netconfDeviceProviderPoll",
173 "polling-executor-%d", log));
Andrea Campanella5c999e22016-03-01 15:12:53 -0800174
Michele Santuari576f09c2016-09-28 14:20:00 +0200175 protected DeviceProviderService providerService;
mskala832d0472017-06-09 16:31:42 +0200176 private final Map<DeviceId, AtomicInteger> retriedPortDiscoveryMap = new ConcurrentHashMap<>();
Michele Santuari576f09c2016-09-28 14:20:00 +0200177 protected ScheduledFuture<?> scheduledTask;
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100178 private final Striped<Lock> deviceLocks = Striped.lock(30);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530179
Ray Milkey02710432018-02-13 17:08:28 -0800180 protected final ConfigFactory factory =
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700181 // TODO consider moving Config registration to NETCONF ctl bundle
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200182 new ConfigFactory<DeviceId, NetconfDeviceConfig>(
183 SubjectFactories.DEVICE_SUBJECT_FACTORY,
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700184 NetconfDeviceConfig.class, NetconfDeviceConfig.CONFIG_KEY) {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200185 @Override
186 public NetconfDeviceConfig createConfig() {
187 return new NetconfDeviceConfig();
188 }
Ray Milkey02710432018-02-13 17:08:28 -0800189 };
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200190
Michele Santuari576f09c2016-09-28 14:20:00 +0200191 protected final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100192 private NetconfDeviceListener innerNodeListener = new InnerNetconfDeviceListener();
193 private InternalDeviceListener deviceListener = new InternalDeviceListener();
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700194 private boolean active;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530195
Sanjay Se8dcfee2015-04-23 10:07:08 +0530196
197 @Activate
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700198 public void activate(ComponentContext context) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700199 active = true;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700200 componentConfigService.registerProperties(getClass());
Sanjay Se8dcfee2015-04-23 10:07:08 +0530201 providerService = providerRegistry.register(this);
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100202 coreService.registerApplication(APP_NAME);
Ray Milkey02710432018-02-13 17:08:28 -0800203 cfgService.registerConfigFactory(factory);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700204 cfgService.addListener(cfgListener);
andreaeb70a942015-10-16 21:34:46 -0700205 controller.addDeviceListener(innerNodeListener);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700206 deviceService.addListener(deviceListener);
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100207 pollingExecutor.execute(NetconfDeviceProvider.this::connectDevices);
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700208 modified(context);
Thomas Vachuskad6811712015-04-29 21:37:04 -0700209 log.info("Started");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530210 }
211
andreaeb70a942015-10-16 21:34:46 -0700212
Sanjay Se8dcfee2015-04-23 10:07:08 +0530213 @Deactivate
andreaeb70a942015-10-16 21:34:46 -0700214 public void deactivate() {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100215 cfgService.removeListener(cfgListener);
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700216 componentConfigService.unregisterProperties(getClass(), false);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700217 deviceService.removeListener(deviceListener);
218 active = false;
219 controller.getNetconfDevices().forEach(id -> {
220 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(id.toString()));
221 controller.disconnectDevice(id, true);
222 });
Andrea Campanella86294db2016-03-07 11:42:49 -0800223 controller.removeDeviceListener(innerNodeListener);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530224 providerRegistry.unregister(this);
225 providerService = null;
mskala832d0472017-06-09 16:31:42 +0200226 retriedPortDiscoveryMap.clear();
Ray Milkey02710432018-02-13 17:08:28 -0800227 cfgService.unregisterConfigFactory(factory);
helenyrwufd296b62016-06-22 17:43:02 -0700228 scheduledTask.cancel(true);
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100229 connectionExecutor.shutdown();
230 pollingExecutor.shutdown();
Sanjay Seb5eebb2015-04-24 15:44:50 +0530231 log.info("Stopped");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530232 }
233
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700234
235 @Modified
236 public void modified(ComponentContext context) {
237 if (context != null) {
238 Dictionary<?, ?> properties = context.getProperties();
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700239 pollFrequency = Tools.getIntegerProperty(properties, POLL_FREQUENCY_SECONDS,
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100240 POLL_FREQUENCY_SECONDS_DEFAULT);
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700241 log.info("Configured. Poll frequency is configured to {} seconds", pollFrequency);
mskala832d0472017-06-09 16:31:42 +0200242
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700243 maxRetries = Tools.getIntegerProperty(properties, MAX_RETRIES, MAX_RETRIES_DEFAULT);
mskala832d0472017-06-09 16:31:42 +0200244 log.info("Configured. Number of retries is configured to {} times", maxRetries);
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700245 }
246 if (scheduledTask != null) {
247 scheduledTask.cancel(false);
248 }
249 scheduledTask = schedulePolling();
250 }
251
andreaeb70a942015-10-16 21:34:46 -0700252 public NetconfDeviceProvider() {
Andrea Campanella101417d2015-12-11 17:58:07 -0800253 super(new ProviderId(SCHEME_NAME, DEVICE_PROVIDER_PACKAGE));
Sanjay Se8dcfee2015-04-23 10:07:08 +0530254 }
255
256 @Override
257 public void triggerProbe(DeviceId deviceId) {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100258 //Not implemented, unused in netconf cases.
259 log.debug("Probing {} not implemented, not useful for NETCONF", deviceId);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530260 }
261
262 @Override
263 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100264 log.debug("Request role change {}, {}", deviceId, newRole);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700265 if (active) {
266 switch (newRole) {
267 case MASTER:
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100268 if (controller.getNetconfDevice(deviceId) == null) {
269 connectionExecutor.execute(exceptionSafe(() -> withDeviceLock(
270 () -> initiateConnection(deviceId), deviceId).run()));
271 log.debug("Accepting mastership role change to {} for device {}", newRole, deviceId);
272 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700273 break;
274 case STANDBY:
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100275 //TODO this issue a warning on the first election/connection
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700276 controller.disconnectDevice(deviceId, false);
277 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.STANDBY);
278 //else no-op
279 break;
280 case NONE:
281 controller.disconnectDevice(deviceId, false);
282 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
283 break;
284 default:
285 log.error("Unimplemented Mastership state : {}", newRole);
286
287 }
288 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530289 }
290
291 @Override
292 public boolean isReachable(DeviceId deviceId) {
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700293 boolean sessionExists =
294 Optional.ofNullable(controller.getDevicesMap().get(deviceId))
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100295 .map(NetconfDevice::isActive)
296 .orElse(false);
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700297 if (sessionExists) {
298 return true;
299 }
300
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700301 //FIXME this is a workaround util device state is shared
302 // between controller instances.
303 Device device = deviceService.getDevice(deviceId);
304 String ip;
305 int port;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700306 if (device != null) {
307 ip = device.annotations().value(IPADDRESS);
308 port = Integer.parseInt(device.annotations().value(PORT));
309 } else {
David K. Bainbridge56e90232018-12-18 23:25:08 -0800310 Triple<String, Integer, Optional<String>> info = extractIpPortPath(deviceId);
311 ip = info.getLeft();
312 port = info.getMiddle();
Sanjay Se8dcfee2015-04-23 10:07:08 +0530313 }
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700314 // FIXME just opening TCP session probably is not the appropriate
315 // method to test reachability.
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700316 //test connection to device opening a socket to it.
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700317 log.debug("Testing reachability for {}:{}", ip, port);
Andrea Campanella42e7b862018-09-21 11:56:48 +0200318 Socket socket = new Socket();
319 try {
320 socket.connect(new InetSocketAddress(ip, port), 1000);
Yuta HIGUCHI0454d702017-03-17 10:08:38 -0700321 log.debug("rechability of {}, {}, {}", deviceId, socket.isConnected(), !socket.isClosed());
Andrea Campanella42e7b862018-09-21 11:56:48 +0200322 boolean isConnected = socket.isConnected() && !socket.isClosed();
323 socket.close();
324 return isConnected;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700325 } catch (IOException e) {
326 log.info("Device {} is not reachable", deviceId);
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700327 log.debug(" error details", e);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700328 return false;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700329 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530330 }
331
Saurav Dasa2d37502016-03-25 17:50:40 -0700332 @Override
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100333 public void changePortState(DeviceId deviceId, PortNumber portNumber, boolean enable) {
Andrea Campanella32813682017-10-23 15:29:24 +0200334 Device device = deviceService.getDevice(deviceId);
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100335 if (device == null) {
336 log.error("Device {} is not present in the store", deviceId);
337 return;
Andrea Campanella32813682017-10-23 15:29:24 +0200338 }
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100339 if (!mastershipService.isLocalMaster(deviceId)) {
340 log.info("Not master but {}, not changing port state", mastershipService.getLocalRole(deviceId));
341 return;
342 }
343 if (!device.is(PortAdmin.class)) {
344 log.warn("Device {} does not support Port Admin", deviceId);
345 return;
346 }
347 PortAdmin portAdmin = device.as(PortAdmin.class);
348 CompletableFuture<Boolean> modified;
349 if (enable) {
350 modified = portAdmin.enable(portNumber);
351 } else {
352 modified = portAdmin.disable(portNumber);
353 }
354 modified.thenAccept(result -> {
355 if (result) {
356 Port port = deviceService.getPort(deviceId, portNumber);
357 //rebuilding port description with admin state changed.
358 providerService.portStatusChanged(deviceId,
359 DefaultPortDescription.builder()
360 .withPortNumber(portNumber)
361 .isEnabled(enable)
362 .isRemoved(false)
363 .type(port.type())
364 .portSpeed(port.portSpeed())
365 .annotations((SparseAnnotations) port.annotations())
366 .build());
367 } else {
368 log.warn("Your device {} port {} status can't be changed to {}",
369 deviceId, portNumber, enable);
370 }
371 });
Saurav Dasa2d37502016-03-25 17:50:40 -0700372 }
373
Thomas Vachuska5b38dc02018-05-10 15:24:40 -0700374 @Override
375 public void triggerDisconnect(DeviceId deviceId) {
376 log.debug("Forcing disconnect for device {}", deviceId);
377 controller.disconnectDevice(deviceId, true);
378 }
379
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100380 private ScheduledFuture schedulePolling() {
381 return pollingExecutor.scheduleAtFixedRate(exceptionSafe(this::checkAndUpdateDevices),
382 pollFrequency / 10,
383 pollFrequency, TimeUnit.SECONDS);
andreaeb70a942015-10-16 21:34:46 -0700384 }
385
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100386 private Runnable exceptionSafe(Runnable runnable) {
387 return () -> {
388 try {
389 runnable.run();
390 } catch (Exception e) {
391 log.error("Unhandled Exception", e);
392 }
393 };
394 }
395
396 //Connecting devices with initial config
andreaeb70a942015-10-16 21:34:46 -0700397 private void connectDevices() {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100398 Set<DeviceId> deviceSubjects = cfgService.getSubjects(DeviceId.class, NetconfDeviceConfig.class);
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200399 deviceSubjects.forEach(deviceId -> {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100400 connectionExecutor.execute(exceptionSafe(() -> runElectionFor(deviceId)));
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200401 });
helenyrwufd296b62016-06-22 17:43:02 -0700402 }
403
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100404 //updating keys and device info
405 private void checkAndUpdateDevices() {
406 Set<DeviceId> deviceSubjects = cfgService.getSubjects(DeviceId.class, NetconfDeviceConfig.class);
407 deviceSubjects.forEach(deviceId -> {
408 log.debug("check and update {}", deviceId);
409 NetconfDeviceConfig config = cfgService.getConfig(deviceId, NetconfDeviceConfig.class);
410 storeDeviceKey(config.sshKey(), config.username(), config.password(), deviceId);
411 discoverOrUpdatePorts(deviceId);
412 });
413 }
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700414
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100415 //Saving device keys in the store
416 private void storeDeviceKey(String sshKey, String username, String password, DeviceId deviceId) {
417 if (sshKey.equals("")) {
418 deviceKeyAdminService.addKey(
419 DeviceKey.createDeviceKeyUsingUsernamePassword(
420 DeviceKeyId.deviceKeyId(deviceId.toString()), null, username, password));
421 } else {
422 deviceKeyAdminService.addKey(
423 DeviceKey.createDeviceKeyUsingSshKey(
424 DeviceKeyId.deviceKeyId(deviceId.toString()), null, username, password, sshKey));
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700425 }
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100426 }
427
428 //running an election and applying the role to a given device
429 private void runElectionFor(DeviceId deviceId) {
430 //Triggering an election for the deviceId thus only master will connect
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700431 if (!deviceId.uri().getScheme().equals(SCHEME_NAME)) {
432 // not under my scheme, skipping
David K. Bainbridge1070de92019-01-29 22:23:11 -0800433 log.debug("{} not of schema {}, skipping", deviceId, SCHEME_NAME);
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700434 return;
435 }
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100436 connectionExecutor.submit(exceptionSafe(() -> {
437 CompletableFuture<MastershipRole> role = mastershipService.requestRoleFor(deviceId);
438 try {
439 roleChanged(deviceId, role.get());
440 } catch (InterruptedException | ExecutionException e) {
441 log.error("Can't get role for {} ", deviceId, e);
442 }
443 }));
444 }
445
446 //initiating the SSh connection the a given device.
447 private void initiateConnection(DeviceId deviceId) {
448
Andrea Campanella42e7b862018-09-21 11:56:48 +0200449 if (!isReachable(deviceId)) {
450 log.warn("Can't connect to device {}", deviceId);
451 return;
452 }
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100453
454 try {
455 NetconfDevice deviceNetconf = controller.connectDevice(deviceId);
456 if (deviceNetconf != null) {
457 //storeDeviceKey(config.sshKey(), config.username(), config.password(), deviceId);
458 NetconfDeviceConfig config = cfgService.getConfig(deviceId, NetconfDeviceConfig.class);
459 //getting the device description
460 DeviceDescription deviceDescription = getDeviceDescription(deviceId, config);
461 //connecting device to ONOS
462 log.debug("Connected NETCONF device {}, on {}:{} with username {}",
463 deviceId, config.ip(), config.port(),
464 (config.path().isPresent() ? "/" + config.path().get() : ""),
465 config.username());
466 providerService.deviceConnected(deviceId, deviceDescription);
467 } else {
468 mastershipService.relinquishMastership(deviceId);
469 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(deviceId.toString()));
470 log.error("Can't connect to NETCONF device {}", deviceId);
471 }
472 } catch (Exception e) {
473 mastershipService.relinquishMastership(deviceId);
474 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(deviceId.toString()));
475 throw new IllegalStateException(new NetconfException(
476 "Can't connect to NETCONF device " + deviceId, e));
477
478 }
479
480 }
481
482 private DeviceDescription getDeviceDescription(DeviceId deviceId, NetconfDeviceConfig config) {
483 Driver driver = driverService.getDriver(deviceId);
484 if (driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
485 final DriverData data = new DefaultDriverData(driver, deviceId);
486 final DriverHandler handler = new DefaultDriverHandler(data);
487 //creating the behaviour because the core has yet no notion of device.
488 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
489 driver.createBehaviour(handler, DeviceDescriptionDiscovery.class);
490 return getDeviceRepresentation(deviceId, config, deviceDescriptionDiscovery);
491 } else {
Andrea Campanella7eb01712019-02-06 10:02:15 +0100492 return existingOrEmptyDescription(deviceId, config);
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700493 }
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700494 }
495
Andrea Campanella7eb01712019-02-06 10:02:15 +0100496 private DeviceDescription getDeviceRepresentation(DeviceId deviceId, NetconfDeviceConfig config,
497 DeviceDescriptionDiscovery deviceDescriptionDiscovery) {
498
499 DeviceDescription existingOrEmptyDescription = existingOrEmptyDescription(deviceId, config);
500 DeviceDescription newDescription = deviceDescriptionDiscovery.discoverDeviceDetails();
501 if (newDescription == null) {
502 return existingOrEmptyDescription;
503 }
504 //merging and returning
505 return new DefaultDeviceDescription(newDescription, true,
506 DefaultAnnotations.merge((DefaultAnnotations) newDescription.annotations(),
507 existingOrEmptyDescription.annotations()));
508 }
509
510 private DeviceDescription existingOrEmptyDescription(DeviceId deviceId, NetconfDeviceConfig config) {
511 Device device = deviceService.getDevice(deviceId);
512
513 if (deviceService.getDevice(deviceId) != null) {
514 //getting the previous description
515 return new DefaultDeviceDescription(device.id().uri(), device.type(),
516 device.manufacturer(), device.hwVersion(),
517 device.swVersion(), device.serialNumber(),
518 device.chassisId(), (SparseAnnotations) device.annotations());
519 }
520
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100521 ChassisId cid = new ChassisId();
522 String ipAddress = config.ip().toString();
523 DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
524 .set(IPADDRESS, ipAddress)
525 .set(PORT, String.valueOf(config.port()))
526 .set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase());
527 if (config.path().isPresent()) {
528 annotations.set(PATH, config.path().get());
529 }
530 return new DefaultDeviceDescription(deviceId.uri(), Device.Type.SWITCH,
531 UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, cid, true, annotations.build());
532 }
533
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100534
535 private void discoverOrUpdatePorts(DeviceId deviceId) {
536 retriedPortDiscoveryMap.put(deviceId, new AtomicInteger(0));
537 AtomicInteger count = retriedPortDiscoveryMap.get(deviceId);
538 //TODO this does not enable port discovery if port changes.
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530539 Device device = deviceService.getDevice(deviceId);
540 if (device == null) {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100541 log.debug("Cant' reach device {}, not updating ports", deviceId);
Andrea Campanella105736e2017-11-23 12:52:43 +0100542 return;
543 }
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100544 if (deviceService.getPorts(deviceId).isEmpty()
545 && count != null && count.getAndIncrement() < maxRetries) {
546 if (device.is(DeviceDescriptionDiscovery.class)) {
547 providerService.updatePorts(deviceId,
548 device.as(DeviceDescriptionDiscovery.class).discoverPortDetails());
549 } else {
550 log.warn("No DeviceDescirption behaviour for device {}", deviceId);
Andrea Campanella105736e2017-11-23 12:52:43 +0100551 }
Andrea Campanella105736e2017-11-23 12:52:43 +0100552
helenyrwufd296b62016-06-22 17:43:02 -0700553 }
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100554 updatePortStatistics(device);
helenyrwufd296b62016-06-22 17:43:02 -0700555 }
556
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530557 private void updatePortStatistics(Device device) {
558 if (device.is(PortStatisticsDiscovery.class)) {
559 PortStatisticsDiscovery d = device.as(PortStatisticsDiscovery.class);
Andrea Campanellac3627842017-04-04 18:06:54 +0200560 Collection<PortStatistics> portStatistics = d.discoverPortStatistics();
561 if (portStatistics != null) {
562 providerService.updatePortStatistics(device.id(),
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100563 portStatistics);
Andrea Campanellac3627842017-04-04 18:06:54 +0200564 }
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530565 } else {
Yuta HIGUCHIe4cb8cf2017-05-01 22:18:28 -0700566 log.debug("No port statistics getter behaviour for device {}",
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100567 device.id());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700568 }
569 }
570
Andrea Campanella42e7b862018-09-21 11:56:48 +0200571 private <U> U withDeviceLock(Supplier<U> task, DeviceId deviceId) {
572 final Lock lock = deviceLocks.get(deviceId);
573 lock.lock();
574 try {
575 return task.get();
576 } finally {
577 lock.unlock();
578 }
579 }
580
581 private Runnable withDeviceLock(Runnable task, DeviceId deviceId) {
582 // Wrapper of withDeviceLock(Supplier, ...) for void tasks.
583 return () -> withDeviceLock(() -> {
584 task.run();
585 return null;
586 }, deviceId);
587 }
588
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700589 /**
590 * Listener for configuration events.
591 */
andreaeb70a942015-10-16 21:34:46 -0700592 private class InternalNetworkConfigListener implements NetworkConfigListener {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530593
andreaeb70a942015-10-16 21:34:46 -0700594 @Override
595 public void event(NetworkConfigEvent event) {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100596 if (event.configClass().equals(NetconfDeviceConfig.class) && event.config().isPresent()) {
597 connectionExecutor.execute(exceptionSafe(() ->
598 runElectionFor((DeviceId) event.config().get().subject())));
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200599 } else {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100600 log.warn("Incorrect or absent Class for Netconf Configuration");
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200601 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530602 }
603
andreaeb70a942015-10-16 21:34:46 -0700604 @Override
605 public boolean isRelevant(NetworkConfigEvent event) {
Ray Milkey02710432018-02-13 17:08:28 -0800606 return (event.configClass().equals(NetconfDeviceConfig.class)) &&
andreaeb70a942015-10-16 21:34:46 -0700607 (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
608 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530609 }
610 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700611
612 /**
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100613 * Listener for Netconf Controller Events.
614 */
615 private class InnerNetconfDeviceListener implements NetconfDeviceListener {
616
617 @Override
618 public void deviceAdded(DeviceId deviceId) {
619 //no-op
620 log.debug("Netconf device {} added to Netconf controller", deviceId);
621 }
622
623 @Override
624 public void deviceRemoved(DeviceId deviceId) {
625 Preconditions.checkNotNull(deviceId, ISNULL);
626
627 if (deviceService.getDevice(deviceId) != null) {
628 providerService.deviceDisconnected(deviceId);
629 retriedPortDiscoveryMap.remove(deviceId);
630 log.debug("Netconf device {} removed from Netconf controller", deviceId);
631 } else {
632 log.warn("Netconf device {} does not exist in the store, " +
633 "it may already have been removed", deviceId);
634 }
635 }
636 }
637
638 /**
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700639 * Listener for core device events.
640 */
641 private class InternalDeviceListener implements DeviceListener {
642 @Override
643 public void event(DeviceEvent event) {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100644 connectionExecutor.submit(exceptionSafe(() -> discoverOrUpdatePorts(event.subject().id())));
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700645 }
646
647 @Override
648 public boolean isRelevant(DeviceEvent event) {
Andrea Campanella42e7b862018-09-21 11:56:48 +0200649 if (event.type() != DeviceEvent.Type.DEVICE_ADDED &&
650 event.type() != DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED) {
Thomas Vachuska5b38dc02018-05-10 15:24:40 -0700651 return false;
652 }
Andrea Campanella968f93f2017-06-08 11:09:28 +0200653 return (SCHEME_NAME.equalsIgnoreCase(event.subject().annotations().value(AnnotationKeys.PROTOCOL)) ||
654 (SCHEME_NAME.equalsIgnoreCase(event.subject().id().uri().getScheme()))) &&
Michele Santuari576f09c2016-09-28 14:20:00 +0200655 mastershipService.isLocalMaster(event.subject().id());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700656 }
657 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530658}