blob: f52e61253359dd0e398f8a51afb7bfe9d3358c36 [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;
DongRyeol Cha527e7b22019-04-19 13:24:53 +090088import java.util.concurrent.CancellationException;
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +010089import java.util.concurrent.ExecutionException;
Andrea Campanella5c999e22016-03-01 15:12:53 -080090import java.util.concurrent.ExecutorService;
91import java.util.concurrent.Executors;
DongRyeol Cha527e7b22019-04-19 13:24:53 +090092import java.util.concurrent.ForkJoinPool;
helenyrwufd296b62016-06-22 17:43:02 -070093import java.util.concurrent.ScheduledExecutorService;
94import java.util.concurrent.ScheduledFuture;
95import java.util.concurrent.TimeUnit;
mskala832d0472017-06-09 16:31:42 +020096import java.util.concurrent.atomic.AtomicInteger;
Andrea Campanella42e7b862018-09-21 11:56:48 +020097import java.util.concurrent.locks.Lock;
98import java.util.function.Supplier;
andreaeb70a942015-10-16 21:34:46 -070099
Yuta HIGUCHI1624df12016-07-21 16:54:33 -0700100import static java.util.concurrent.Executors.newScheduledThreadPool;
Andrea Campanella5c999e22016-03-01 15:12:53 -0800101import static org.onlab.util.Tools.groupedThreads;
David K. Bainbridge56e90232018-12-18 23:25:08 -0800102import static org.onosproject.netconf.NetconfDeviceInfo.extractIpPortPath;
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100103import static org.onosproject.provider.netconf.device.impl.OsgiPropertyConstants.MAX_RETRIES;
104import static org.onosproject.provider.netconf.device.impl.OsgiPropertyConstants.MAX_RETRIES_DEFAULT;
105import static org.onosproject.provider.netconf.device.impl.OsgiPropertyConstants.POLL_FREQUENCY_SECONDS;
106import static org.onosproject.provider.netconf.device.impl.OsgiPropertyConstants.POLL_FREQUENCY_SECONDS_DEFAULT;
andreaeb70a942015-10-16 21:34:46 -0700107import static org.slf4j.LoggerFactory.getLogger;
108
Sanjay Se8dcfee2015-04-23 10:07:08 +0530109/**
andreaeb70a942015-10-16 21:34:46 -0700110 * Provider which uses an NETCONF controller to detect device.
Sanjay Se8dcfee2015-04-23 10:07:08 +0530111 */
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700112@Component(immediate = true,
113 property = {
Andrea Campanella02f86c82019-03-18 16:57:27 -0700114 POLL_FREQUENCY_SECONDS + ":Integer=" + POLL_FREQUENCY_SECONDS_DEFAULT,
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700115 MAX_RETRIES + ":Integer=" + MAX_RETRIES_DEFAULT,
116 })
Sanjay Se8dcfee2015-04-23 10:07:08 +0530117public class NetconfDeviceProvider extends AbstractProvider
118 implements DeviceProvider {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700119
andreaeb70a942015-10-16 21:34:46 -0700120 private final Logger log = getLogger(getClass());
Sanjay Se8dcfee2015-04-23 10:07:08 +0530121
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700122 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Sanjay Se8dcfee2015-04-23 10:07:08 +0530123 protected DeviceProviderRegistry providerRegistry;
124
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700125 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella101417d2015-12-11 17:58:07 -0800126 protected NetconfController controller;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530127
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700128 @Reference(cardinality = ReferenceCardinality.MANDATORY)
andreaeb70a942015-10-16 21:34:46 -0700129 protected NetworkConfigRegistry cfgService;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530130
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700131 @Reference(cardinality = ReferenceCardinality.MANDATORY)
andreaeb70a942015-10-16 21:34:46 -0700132 protected CoreService coreService;
Thomas Vachuskad6811712015-04-29 21:37:04 -0700133
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700134 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700135 protected DeviceService deviceService;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -0800136
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700137 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100138 protected DriverService driverService;
139
140 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700141 protected DeviceKeyAdminService deviceKeyAdminService;
142
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700143 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700144 protected MastershipService mastershipService;
145
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700146 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700147 protected ComponentConfigService componentConfigService;
148
149
Michele Santuari576f09c2016-09-28 14:20:00 +0200150 protected static final String APP_NAME = "org.onosproject.netconf";
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200151 protected static final String SCHEME_NAME = "netconf";
Andrea Campanella101417d2015-12-11 17:58:07 -0800152 private static final String DEVICE_PROVIDER_PACKAGE = "org.onosproject.netconf.provider.device";
153 private static final String UNKNOWN = "unknown";
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700154 protected static final String ISNULL = "NetconfDeviceInfo is null";
155 private static final String IPADDRESS = "ipaddress";
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700156 private static final String PORT = "port";
David K. Bainbridge56e90232018-12-18 23:25:08 -0800157 private static final String PATH = "path";
helenyrwufd296b62016-06-22 17:43:02 -0700158 private static final int CORE_POOL_SIZE = 10;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700159
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100160 /**
161 * Configure poll frequency for port status and statistics; default is 30 sec.
162 */
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700163 private int pollFrequency = POLL_FREQUENCY_SECONDS_DEFAULT;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530164
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100165 /**
166 * Configure maximum allowed number of retries for obtaining list of ports; default is 5 times.
167 */
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700168 private int maxRetries = MAX_RETRIES_DEFAULT;
mskala832d0472017-06-09 16:31:42 +0200169
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100170 protected ExecutorService connectionExecutor = Executors.newFixedThreadPool(CORE_POOL_SIZE,
171 groupedThreads("onos/netconfDeviceProviderConnection",
172 "connection-executor-%d", log));
173 protected ScheduledExecutorService pollingExecutor = newScheduledThreadPool(CORE_POOL_SIZE,
174 groupedThreads("onos/netconfDeviceProviderPoll",
175 "polling-executor-%d", log));
Andrea Campanella5c999e22016-03-01 15:12:53 -0800176
Michele Santuari576f09c2016-09-28 14:20:00 +0200177 protected DeviceProviderService providerService;
mskala832d0472017-06-09 16:31:42 +0200178 private final Map<DeviceId, AtomicInteger> retriedPortDiscoveryMap = new ConcurrentHashMap<>();
Michele Santuari576f09c2016-09-28 14:20:00 +0200179 protected ScheduledFuture<?> scheduledTask;
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100180 private final Striped<Lock> deviceLocks = Striped.lock(30);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530181
Ray Milkey02710432018-02-13 17:08:28 -0800182 protected final ConfigFactory factory =
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700183 // TODO consider moving Config registration to NETCONF ctl bundle
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200184 new ConfigFactory<DeviceId, NetconfDeviceConfig>(
185 SubjectFactories.DEVICE_SUBJECT_FACTORY,
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700186 NetconfDeviceConfig.class, NetconfDeviceConfig.CONFIG_KEY) {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200187 @Override
188 public NetconfDeviceConfig createConfig() {
189 return new NetconfDeviceConfig();
190 }
Ray Milkey02710432018-02-13 17:08:28 -0800191 };
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200192
Michele Santuari576f09c2016-09-28 14:20:00 +0200193 protected final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100194 private NetconfDeviceListener innerNodeListener = new InnerNetconfDeviceListener();
195 private InternalDeviceListener deviceListener = new InternalDeviceListener();
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700196 private boolean active;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530197
DongRyeol Cha527e7b22019-04-19 13:24:53 +0900198 private ForkJoinPool scheduledTaskPool = new ForkJoinPool(CORE_POOL_SIZE);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530199
200 @Activate
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700201 public void activate(ComponentContext context) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700202 active = true;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700203 componentConfigService.registerProperties(getClass());
Sanjay Se8dcfee2015-04-23 10:07:08 +0530204 providerService = providerRegistry.register(this);
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100205 coreService.registerApplication(APP_NAME);
Ray Milkey02710432018-02-13 17:08:28 -0800206 cfgService.registerConfigFactory(factory);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700207 cfgService.addListener(cfgListener);
andreaeb70a942015-10-16 21:34:46 -0700208 controller.addDeviceListener(innerNodeListener);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700209 deviceService.addListener(deviceListener);
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100210 pollingExecutor.execute(NetconfDeviceProvider.this::connectDevices);
DongRyeol Cha40259002019-03-15 18:26:11 +0900211 scheduledTask = schedulePolling();
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700212 modified(context);
Thomas Vachuskad6811712015-04-29 21:37:04 -0700213 log.info("Started");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530214 }
215
andreaeb70a942015-10-16 21:34:46 -0700216
Sanjay Se8dcfee2015-04-23 10:07:08 +0530217 @Deactivate
andreaeb70a942015-10-16 21:34:46 -0700218 public void deactivate() {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100219 cfgService.removeListener(cfgListener);
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700220 componentConfigService.unregisterProperties(getClass(), false);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700221 deviceService.removeListener(deviceListener);
222 active = false;
223 controller.getNetconfDevices().forEach(id -> {
224 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(id.toString()));
225 controller.disconnectDevice(id, true);
226 });
Andrea Campanella86294db2016-03-07 11:42:49 -0800227 controller.removeDeviceListener(innerNodeListener);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530228 providerRegistry.unregister(this);
229 providerService = null;
mskala832d0472017-06-09 16:31:42 +0200230 retriedPortDiscoveryMap.clear();
Ray Milkey02710432018-02-13 17:08:28 -0800231 cfgService.unregisterConfigFactory(factory);
helenyrwufd296b62016-06-22 17:43:02 -0700232 scheduledTask.cancel(true);
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100233 connectionExecutor.shutdown();
234 pollingExecutor.shutdown();
Sanjay Seb5eebb2015-04-24 15:44:50 +0530235 log.info("Stopped");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530236 }
237
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700238
239 @Modified
240 public void modified(ComponentContext context) {
241 if (context != null) {
242 Dictionary<?, ?> properties = context.getProperties();
DongRyeol Cha40259002019-03-15 18:26:11 +0900243 int newPollFrequency = Tools.getIntegerProperty(properties, POLL_FREQUENCY_SECONDS,
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100244 POLL_FREQUENCY_SECONDS_DEFAULT);
DongRyeol Cha40259002019-03-15 18:26:11 +0900245
246 if (newPollFrequency != pollFrequency) {
247 pollFrequency = newPollFrequency;
248
249 if (scheduledTask != null) {
250 scheduledTask.cancel(false);
251 }
252 scheduledTask = schedulePolling();
253 log.info("Configured. Poll frequency is configured to {} seconds", pollFrequency);
254 }
mskala832d0472017-06-09 16:31:42 +0200255
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700256 maxRetries = Tools.getIntegerProperty(properties, MAX_RETRIES, MAX_RETRIES_DEFAULT);
mskala832d0472017-06-09 16:31:42 +0200257 log.info("Configured. Number of retries is configured to {} times", maxRetries);
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700258 }
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700259 }
260
andreaeb70a942015-10-16 21:34:46 -0700261 public NetconfDeviceProvider() {
Andrea Campanella101417d2015-12-11 17:58:07 -0800262 super(new ProviderId(SCHEME_NAME, DEVICE_PROVIDER_PACKAGE));
Sanjay Se8dcfee2015-04-23 10:07:08 +0530263 }
264
265 @Override
266 public void triggerProbe(DeviceId deviceId) {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100267 //Not implemented, unused in netconf cases.
268 log.debug("Probing {} not implemented, not useful for NETCONF", deviceId);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530269 }
270
271 @Override
272 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100273 log.debug("Request role change {}, {}", deviceId, newRole);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700274 if (active) {
275 switch (newRole) {
276 case MASTER:
Anurag Chadha01ab40a2020-07-13 13:38:08 +0530277 if (controller.getNetconfDevice(deviceId) == null ||
278 !controller.getNetconfDevice(deviceId).isMasterSession()) {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100279 connectionExecutor.execute(exceptionSafe(() -> withDeviceLock(
280 () -> initiateConnection(deviceId), deviceId).run()));
281 log.debug("Accepting mastership role change to {} for device {}", newRole, deviceId);
282 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700283 break;
284 case STANDBY:
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100285 //TODO this issue a warning on the first election/connection
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700286 controller.disconnectDevice(deviceId, false);
gyewan.an91d7e7e2019-01-17 15:12:48 +0900287 withDeviceLock(
288 () -> initiateConnection(deviceId, newRole), deviceId).run();
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700289 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.STANDBY);
290 //else no-op
291 break;
292 case NONE:
293 controller.disconnectDevice(deviceId, false);
294 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
295 break;
296 default:
297 log.error("Unimplemented Mastership state : {}", newRole);
298
299 }
300 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530301 }
302
303 @Override
304 public boolean isReachable(DeviceId deviceId) {
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700305 boolean sessionExists =
306 Optional.ofNullable(controller.getDevicesMap().get(deviceId))
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100307 .map(NetconfDevice::isActive)
308 .orElse(false);
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700309 if (sessionExists) {
310 return true;
311 }
312
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700313 //FIXME this is a workaround util device state is shared
314 // between controller instances.
315 Device device = deviceService.getDevice(deviceId);
316 String ip;
317 int port;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700318 if (device != null) {
319 ip = device.annotations().value(IPADDRESS);
320 port = Integer.parseInt(device.annotations().value(PORT));
321 } else {
David K. Bainbridge56e90232018-12-18 23:25:08 -0800322 Triple<String, Integer, Optional<String>> info = extractIpPortPath(deviceId);
323 ip = info.getLeft();
324 port = info.getMiddle();
Sanjay Se8dcfee2015-04-23 10:07:08 +0530325 }
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700326 // FIXME just opening TCP session probably is not the appropriate
327 // method to test reachability.
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700328 //test connection to device opening a socket to it.
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700329 log.debug("Testing reachability for {}:{}", ip, port);
Andrea Campanella42e7b862018-09-21 11:56:48 +0200330 Socket socket = new Socket();
331 try {
332 socket.connect(new InetSocketAddress(ip, port), 1000);
Yuta HIGUCHI0454d702017-03-17 10:08:38 -0700333 log.debug("rechability of {}, {}, {}", deviceId, socket.isConnected(), !socket.isClosed());
Andrea Campanella42e7b862018-09-21 11:56:48 +0200334 boolean isConnected = socket.isConnected() && !socket.isClosed();
335 socket.close();
336 return isConnected;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700337 } catch (IOException e) {
338 log.info("Device {} is not reachable", deviceId);
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700339 log.debug(" error details", e);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700340 return false;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700341 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530342 }
343
Saurav Dasa2d37502016-03-25 17:50:40 -0700344 @Override
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100345 public void changePortState(DeviceId deviceId, PortNumber portNumber, boolean enable) {
Andrea Campanella32813682017-10-23 15:29:24 +0200346 Device device = deviceService.getDevice(deviceId);
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100347 if (device == null) {
348 log.error("Device {} is not present in the store", deviceId);
349 return;
Andrea Campanella32813682017-10-23 15:29:24 +0200350 }
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100351 if (!mastershipService.isLocalMaster(deviceId)) {
352 log.info("Not master but {}, not changing port state", mastershipService.getLocalRole(deviceId));
353 return;
354 }
355 if (!device.is(PortAdmin.class)) {
356 log.warn("Device {} does not support Port Admin", deviceId);
357 return;
358 }
359 PortAdmin portAdmin = device.as(PortAdmin.class);
360 CompletableFuture<Boolean> modified;
361 if (enable) {
362 modified = portAdmin.enable(portNumber);
363 } else {
364 modified = portAdmin.disable(portNumber);
365 }
366 modified.thenAccept(result -> {
367 if (result) {
368 Port port = deviceService.getPort(deviceId, portNumber);
369 //rebuilding port description with admin state changed.
370 providerService.portStatusChanged(deviceId,
371 DefaultPortDescription.builder()
372 .withPortNumber(portNumber)
373 .isEnabled(enable)
374 .isRemoved(false)
375 .type(port.type())
376 .portSpeed(port.portSpeed())
377 .annotations((SparseAnnotations) port.annotations())
378 .build());
379 } else {
380 log.warn("Your device {} port {} status can't be changed to {}",
381 deviceId, portNumber, enable);
382 }
383 });
Saurav Dasa2d37502016-03-25 17:50:40 -0700384 }
385
Thomas Vachuska5b38dc02018-05-10 15:24:40 -0700386 @Override
387 public void triggerDisconnect(DeviceId deviceId) {
388 log.debug("Forcing disconnect for device {}", deviceId);
389 controller.disconnectDevice(deviceId, true);
390 }
391
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100392 private ScheduledFuture schedulePolling() {
393 return pollingExecutor.scheduleAtFixedRate(exceptionSafe(this::checkAndUpdateDevices),
394 pollFrequency / 10,
395 pollFrequency, TimeUnit.SECONDS);
andreaeb70a942015-10-16 21:34:46 -0700396 }
397
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100398 private Runnable exceptionSafe(Runnable runnable) {
399 return () -> {
400 try {
401 runnable.run();
402 } catch (Exception e) {
403 log.error("Unhandled Exception", e);
404 }
405 };
406 }
407
408 //Connecting devices with initial config
andreaeb70a942015-10-16 21:34:46 -0700409 private void connectDevices() {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100410 Set<DeviceId> deviceSubjects = cfgService.getSubjects(DeviceId.class, NetconfDeviceConfig.class);
DongRyeol Cha07f0d372019-04-03 12:29:00 +0900411 deviceSubjects.parallelStream().forEach(deviceId -> {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100412 connectionExecutor.execute(exceptionSafe(() -> runElectionFor(deviceId)));
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200413 });
helenyrwufd296b62016-06-22 17:43:02 -0700414 }
415
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100416 //updating keys and device info
417 private void checkAndUpdateDevices() {
418 Set<DeviceId> deviceSubjects = cfgService.getSubjects(DeviceId.class, NetconfDeviceConfig.class);
DongRyeol Cha527e7b22019-04-19 13:24:53 +0900419 try {
420 scheduledTaskPool.submit(() -> {
421 deviceSubjects.parallelStream().forEach(deviceId -> {
422 log.debug("check and update {}", deviceId);
423 NetconfDeviceConfig config = cfgService.getConfig(deviceId, NetconfDeviceConfig.class);
424 storeDeviceKey(config.sshKey(), config.username(), config.password(), deviceId);
425 discoverOrUpdatePorts(deviceId);
426 });
427 }).get();
428 } catch (ExecutionException e) {
429 log.error("Can't update the devices due to {}", e.getMessage());
430 } catch (InterruptedException | CancellationException e) {
431 log.info("Update device is cancelled due to {}", e.getMessage());
432 }
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100433 }
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700434
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100435 //Saving device keys in the store
436 private void storeDeviceKey(String sshKey, String username, String password, DeviceId deviceId) {
437 if (sshKey.equals("")) {
438 deviceKeyAdminService.addKey(
439 DeviceKey.createDeviceKeyUsingUsernamePassword(
440 DeviceKeyId.deviceKeyId(deviceId.toString()), null, username, password));
441 } else {
442 deviceKeyAdminService.addKey(
443 DeviceKey.createDeviceKeyUsingSshKey(
444 DeviceKeyId.deviceKeyId(deviceId.toString()), null, username, password, sshKey));
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700445 }
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100446 }
447
448 //running an election and applying the role to a given device
449 private void runElectionFor(DeviceId deviceId) {
450 //Triggering an election for the deviceId thus only master will connect
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700451 if (!deviceId.uri().getScheme().equals(SCHEME_NAME)) {
452 // not under my scheme, skipping
David K. Bainbridge1070de92019-01-29 22:23:11 -0800453 log.debug("{} not of schema {}, skipping", deviceId, SCHEME_NAME);
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700454 return;
455 }
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100456 connectionExecutor.submit(exceptionSafe(() -> {
457 CompletableFuture<MastershipRole> role = mastershipService.requestRoleFor(deviceId);
458 try {
459 roleChanged(deviceId, role.get());
460 } catch (InterruptedException | ExecutionException e) {
461 log.error("Can't get role for {} ", deviceId, e);
462 }
463 }));
464 }
465
466 //initiating the SSh connection the a given device.
467 private void initiateConnection(DeviceId deviceId) {
468
Andrea Campanella42e7b862018-09-21 11:56:48 +0200469 if (!isReachable(deviceId)) {
470 log.warn("Can't connect to device {}", deviceId);
471 return;
472 }
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100473
474 try {
475 NetconfDevice deviceNetconf = controller.connectDevice(deviceId);
476 if (deviceNetconf != null) {
477 //storeDeviceKey(config.sshKey(), config.username(), config.password(), deviceId);
478 NetconfDeviceConfig config = cfgService.getConfig(deviceId, NetconfDeviceConfig.class);
479 //getting the device description
480 DeviceDescription deviceDescription = getDeviceDescription(deviceId, config);
481 //connecting device to ONOS
Kieran McPeakee1b418f2019-05-23 13:42:13 +0100482 log.debug("Connected NETCONF device {}, on {}:{} {} with username {}",
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100483 deviceId, config.ip(), config.port(),
484 (config.path().isPresent() ? "/" + config.path().get() : ""),
485 config.username());
486 providerService.deviceConnected(deviceId, deviceDescription);
487 } else {
488 mastershipService.relinquishMastership(deviceId);
489 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(deviceId.toString()));
490 log.error("Can't connect to NETCONF device {}", deviceId);
491 }
492 } catch (Exception e) {
493 mastershipService.relinquishMastership(deviceId);
494 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(deviceId.toString()));
495 throw new IllegalStateException(new NetconfException(
496 "Can't connect to NETCONF device " + deviceId, e));
497
498 }
499
500 }
501
502 private DeviceDescription getDeviceDescription(DeviceId deviceId, NetconfDeviceConfig config) {
503 Driver driver = driverService.getDriver(deviceId);
504 if (driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
505 final DriverData data = new DefaultDriverData(driver, deviceId);
506 final DriverHandler handler = new DefaultDriverHandler(data);
507 //creating the behaviour because the core has yet no notion of device.
508 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
509 driver.createBehaviour(handler, DeviceDescriptionDiscovery.class);
510 return getDeviceRepresentation(deviceId, config, deviceDescriptionDiscovery);
511 } else {
Andrea Campanella7eb01712019-02-06 10:02:15 +0100512 return existingOrEmptyDescription(deviceId, config);
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700513 }
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700514 }
515
Andrea Campanella7eb01712019-02-06 10:02:15 +0100516 private DeviceDescription getDeviceRepresentation(DeviceId deviceId, NetconfDeviceConfig config,
517 DeviceDescriptionDiscovery deviceDescriptionDiscovery) {
518
519 DeviceDescription existingOrEmptyDescription = existingOrEmptyDescription(deviceId, config);
520 DeviceDescription newDescription = deviceDescriptionDiscovery.discoverDeviceDetails();
521 if (newDescription == null) {
522 return existingOrEmptyDescription;
523 }
524 //merging and returning
525 return new DefaultDeviceDescription(newDescription, true,
526 DefaultAnnotations.merge((DefaultAnnotations) newDescription.annotations(),
527 existingOrEmptyDescription.annotations()));
528 }
529
530 private DeviceDescription existingOrEmptyDescription(DeviceId deviceId, NetconfDeviceConfig config) {
531 Device device = deviceService.getDevice(deviceId);
532
533 if (deviceService.getDevice(deviceId) != null) {
534 //getting the previous description
535 return new DefaultDeviceDescription(device.id().uri(), device.type(),
536 device.manufacturer(), device.hwVersion(),
537 device.swVersion(), device.serialNumber(),
538 device.chassisId(), (SparseAnnotations) device.annotations());
539 }
540
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100541 ChassisId cid = new ChassisId();
542 String ipAddress = config.ip().toString();
543 DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
544 .set(IPADDRESS, ipAddress)
545 .set(PORT, String.valueOf(config.port()))
546 .set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase());
547 if (config.path().isPresent()) {
548 annotations.set(PATH, config.path().get());
549 }
550 return new DefaultDeviceDescription(deviceId.uri(), Device.Type.SWITCH,
551 UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, cid, true, annotations.build());
552 }
553
gyewan.an91d7e7e2019-01-17 15:12:48 +0900554 /**
555 * Will appropriately create connections with the device.
556 * For Master role: will create secure transport and proxy sessions.
557 * For Standby role: will create only proxy session and disconnect secure transport session.
558 * For none role: will disconnect all sessions.
559 *
560 * @param deviceId device id
561 * @param newRole new role
562 */
563 private void initiateConnection(DeviceId deviceId, MastershipRole newRole) {
564 try {
565 if (isReachable(deviceId)) {
566 NetconfDevice device = null;
567 if (newRole.equals(MastershipRole.MASTER)) {
568 device = controller.connectDevice(deviceId, true);
569 } else if (newRole.equals(MastershipRole.STANDBY)) {
570 device = controller.connectDevice(deviceId, false);
571 }
572
573 if (device != null) {
574 providerService.receivedRoleReply(deviceId, newRole, newRole);
575 } else {
576 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
577 }
578
579 }
580 } catch (Exception e) {
581 if (deviceService.getDevice(deviceId) != null) {
582 providerService.deviceDisconnected(deviceId);
583 }
584 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(deviceId.toString()));
585 throw new IllegalStateException(new NetconfException(
586 "Can't connect to NETCONF device " + deviceId, e));
587 }
588 }
589
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100590
591 private void discoverOrUpdatePorts(DeviceId deviceId) {
592 retriedPortDiscoveryMap.put(deviceId, new AtomicInteger(0));
593 AtomicInteger count = retriedPortDiscoveryMap.get(deviceId);
594 //TODO this does not enable port discovery if port changes.
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530595 Device device = deviceService.getDevice(deviceId);
596 if (device == null) {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100597 log.debug("Cant' reach device {}, not updating ports", deviceId);
Andrea Campanella105736e2017-11-23 12:52:43 +0100598 return;
599 }
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100600 if (deviceService.getPorts(deviceId).isEmpty()
601 && count != null && count.getAndIncrement() < maxRetries) {
602 if (device.is(DeviceDescriptionDiscovery.class)) {
603 providerService.updatePorts(deviceId,
604 device.as(DeviceDescriptionDiscovery.class).discoverPortDetails());
605 } else {
606 log.warn("No DeviceDescirption behaviour for device {}", deviceId);
Andrea Campanella105736e2017-11-23 12:52:43 +0100607 }
Andrea Campanella105736e2017-11-23 12:52:43 +0100608
helenyrwufd296b62016-06-22 17:43:02 -0700609 }
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100610 updatePortStatistics(device);
helenyrwufd296b62016-06-22 17:43:02 -0700611 }
612
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530613 private void updatePortStatistics(Device device) {
614 if (device.is(PortStatisticsDiscovery.class)) {
615 PortStatisticsDiscovery d = device.as(PortStatisticsDiscovery.class);
Andrea Campanellac3627842017-04-04 18:06:54 +0200616 Collection<PortStatistics> portStatistics = d.discoverPortStatistics();
617 if (portStatistics != null) {
618 providerService.updatePortStatistics(device.id(),
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100619 portStatistics);
Andrea Campanellac3627842017-04-04 18:06:54 +0200620 }
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530621 } else {
Yuta HIGUCHIe4cb8cf2017-05-01 22:18:28 -0700622 log.debug("No port statistics getter behaviour for device {}",
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100623 device.id());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700624 }
625 }
626
Andrea Campanella42e7b862018-09-21 11:56:48 +0200627 private <U> U withDeviceLock(Supplier<U> task, DeviceId deviceId) {
628 final Lock lock = deviceLocks.get(deviceId);
629 lock.lock();
630 try {
631 return task.get();
632 } finally {
633 lock.unlock();
634 }
635 }
636
637 private Runnable withDeviceLock(Runnable task, DeviceId deviceId) {
638 // Wrapper of withDeviceLock(Supplier, ...) for void tasks.
639 return () -> withDeviceLock(() -> {
640 task.run();
641 return null;
642 }, deviceId);
643 }
644
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700645 /**
646 * Listener for configuration events.
647 */
andreaeb70a942015-10-16 21:34:46 -0700648 private class InternalNetworkConfigListener implements NetworkConfigListener {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530649
andreaeb70a942015-10-16 21:34:46 -0700650 @Override
651 public void event(NetworkConfigEvent event) {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100652 if (event.configClass().equals(NetconfDeviceConfig.class) && event.config().isPresent()) {
653 connectionExecutor.execute(exceptionSafe(() ->
654 runElectionFor((DeviceId) event.config().get().subject())));
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200655 } else {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100656 log.warn("Incorrect or absent Class for Netconf Configuration");
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200657 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530658 }
659
andreaeb70a942015-10-16 21:34:46 -0700660 @Override
661 public boolean isRelevant(NetworkConfigEvent event) {
Ray Milkey02710432018-02-13 17:08:28 -0800662 return (event.configClass().equals(NetconfDeviceConfig.class)) &&
andreaeb70a942015-10-16 21:34:46 -0700663 (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
664 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530665 }
666 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700667
668 /**
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100669 * Listener for Netconf Controller Events.
670 */
671 private class InnerNetconfDeviceListener implements NetconfDeviceListener {
672
673 @Override
674 public void deviceAdded(DeviceId deviceId) {
675 //no-op
676 log.debug("Netconf device {} added to Netconf controller", deviceId);
677 }
678
679 @Override
680 public void deviceRemoved(DeviceId deviceId) {
681 Preconditions.checkNotNull(deviceId, ISNULL);
682
683 if (deviceService.getDevice(deviceId) != null) {
684 providerService.deviceDisconnected(deviceId);
685 retriedPortDiscoveryMap.remove(deviceId);
686 log.debug("Netconf device {} removed from Netconf controller", deviceId);
687 } else {
688 log.warn("Netconf device {} does not exist in the store, " +
689 "it may already have been removed", deviceId);
690 }
691 }
692 }
693
694 /**
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700695 * Listener for core device events.
696 */
697 private class InternalDeviceListener implements DeviceListener {
698 @Override
699 public void event(DeviceEvent event) {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100700 connectionExecutor.submit(exceptionSafe(() -> discoverOrUpdatePorts(event.subject().id())));
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700701 }
702
703 @Override
704 public boolean isRelevant(DeviceEvent event) {
Andrea Campanella42e7b862018-09-21 11:56:48 +0200705 if (event.type() != DeviceEvent.Type.DEVICE_ADDED &&
706 event.type() != DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED) {
Thomas Vachuska5b38dc02018-05-10 15:24:40 -0700707 return false;
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}