blob: d09a352fc4a44a67c8b48c81a48a4d0ae6951319 [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:
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100277 if (controller.getNetconfDevice(deviceId) == null) {
278 connectionExecutor.execute(exceptionSafe(() -> withDeviceLock(
279 () -> initiateConnection(deviceId), deviceId).run()));
280 log.debug("Accepting mastership role change to {} for device {}", newRole, deviceId);
281 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700282 break;
283 case STANDBY:
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100284 //TODO this issue a warning on the first election/connection
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700285 controller.disconnectDevice(deviceId, false);
gyewan.an91d7e7e2019-01-17 15:12:48 +0900286 withDeviceLock(
287 () -> initiateConnection(deviceId, newRole), deviceId).run();
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700288 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.STANDBY);
289 //else no-op
290 break;
291 case NONE:
292 controller.disconnectDevice(deviceId, false);
293 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
294 break;
295 default:
296 log.error("Unimplemented Mastership state : {}", newRole);
297
298 }
299 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530300 }
301
302 @Override
303 public boolean isReachable(DeviceId deviceId) {
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700304 boolean sessionExists =
305 Optional.ofNullable(controller.getDevicesMap().get(deviceId))
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100306 .map(NetconfDevice::isActive)
307 .orElse(false);
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700308 if (sessionExists) {
309 return true;
310 }
311
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700312 //FIXME this is a workaround util device state is shared
313 // between controller instances.
314 Device device = deviceService.getDevice(deviceId);
315 String ip;
316 int port;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700317 if (device != null) {
318 ip = device.annotations().value(IPADDRESS);
319 port = Integer.parseInt(device.annotations().value(PORT));
320 } else {
David K. Bainbridge56e90232018-12-18 23:25:08 -0800321 Triple<String, Integer, Optional<String>> info = extractIpPortPath(deviceId);
322 ip = info.getLeft();
323 port = info.getMiddle();
Sanjay Se8dcfee2015-04-23 10:07:08 +0530324 }
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700325 // FIXME just opening TCP session probably is not the appropriate
326 // method to test reachability.
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700327 //test connection to device opening a socket to it.
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700328 log.debug("Testing reachability for {}:{}", ip, port);
Andrea Campanella42e7b862018-09-21 11:56:48 +0200329 Socket socket = new Socket();
330 try {
331 socket.connect(new InetSocketAddress(ip, port), 1000);
Yuta HIGUCHI0454d702017-03-17 10:08:38 -0700332 log.debug("rechability of {}, {}, {}", deviceId, socket.isConnected(), !socket.isClosed());
Andrea Campanella42e7b862018-09-21 11:56:48 +0200333 boolean isConnected = socket.isConnected() && !socket.isClosed();
334 socket.close();
335 return isConnected;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700336 } catch (IOException e) {
337 log.info("Device {} is not reachable", deviceId);
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700338 log.debug(" error details", e);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700339 return false;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700340 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530341 }
342
Saurav Dasa2d37502016-03-25 17:50:40 -0700343 @Override
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100344 public void changePortState(DeviceId deviceId, PortNumber portNumber, boolean enable) {
Andrea Campanella32813682017-10-23 15:29:24 +0200345 Device device = deviceService.getDevice(deviceId);
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100346 if (device == null) {
347 log.error("Device {} is not present in the store", deviceId);
348 return;
Andrea Campanella32813682017-10-23 15:29:24 +0200349 }
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100350 if (!mastershipService.isLocalMaster(deviceId)) {
351 log.info("Not master but {}, not changing port state", mastershipService.getLocalRole(deviceId));
352 return;
353 }
354 if (!device.is(PortAdmin.class)) {
355 log.warn("Device {} does not support Port Admin", deviceId);
356 return;
357 }
358 PortAdmin portAdmin = 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,
370 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());
378 } else {
379 log.warn("Your device {} port {} status can't be changed to {}",
380 deviceId, portNumber, enable);
381 }
382 });
Saurav Dasa2d37502016-03-25 17:50:40 -0700383 }
384
Thomas Vachuska5b38dc02018-05-10 15:24:40 -0700385 @Override
386 public void triggerDisconnect(DeviceId deviceId) {
387 log.debug("Forcing disconnect for device {}", deviceId);
388 controller.disconnectDevice(deviceId, true);
389 }
390
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100391 private ScheduledFuture schedulePolling() {
392 return pollingExecutor.scheduleAtFixedRate(exceptionSafe(this::checkAndUpdateDevices),
393 pollFrequency / 10,
394 pollFrequency, TimeUnit.SECONDS);
andreaeb70a942015-10-16 21:34:46 -0700395 }
396
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100397 private Runnable exceptionSafe(Runnable runnable) {
398 return () -> {
399 try {
400 runnable.run();
401 } catch (Exception e) {
402 log.error("Unhandled Exception", e);
403 }
404 };
405 }
406
407 //Connecting devices with initial config
andreaeb70a942015-10-16 21:34:46 -0700408 private void connectDevices() {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100409 Set<DeviceId> deviceSubjects = cfgService.getSubjects(DeviceId.class, NetconfDeviceConfig.class);
DongRyeol Cha07f0d372019-04-03 12:29:00 +0900410 deviceSubjects.parallelStream().forEach(deviceId -> {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100411 connectionExecutor.execute(exceptionSafe(() -> runElectionFor(deviceId)));
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200412 });
helenyrwufd296b62016-06-22 17:43:02 -0700413 }
414
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100415 //updating keys and device info
416 private void checkAndUpdateDevices() {
417 Set<DeviceId> deviceSubjects = cfgService.getSubjects(DeviceId.class, NetconfDeviceConfig.class);
DongRyeol Cha527e7b22019-04-19 13:24:53 +0900418 try {
419 scheduledTaskPool.submit(() -> {
420 deviceSubjects.parallelStream().forEach(deviceId -> {
421 log.debug("check and update {}", deviceId);
422 NetconfDeviceConfig config = cfgService.getConfig(deviceId, NetconfDeviceConfig.class);
423 storeDeviceKey(config.sshKey(), config.username(), config.password(), deviceId);
424 discoverOrUpdatePorts(deviceId);
425 });
426 }).get();
427 } catch (ExecutionException e) {
428 log.error("Can't update the devices due to {}", e.getMessage());
429 } catch (InterruptedException | CancellationException e) {
430 log.info("Update device is cancelled due to {}", e.getMessage());
431 }
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100432 }
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700433
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100434 //Saving device keys in the store
435 private void storeDeviceKey(String sshKey, String username, String password, DeviceId deviceId) {
436 if (sshKey.equals("")) {
437 deviceKeyAdminService.addKey(
438 DeviceKey.createDeviceKeyUsingUsernamePassword(
439 DeviceKeyId.deviceKeyId(deviceId.toString()), null, username, password));
440 } else {
441 deviceKeyAdminService.addKey(
442 DeviceKey.createDeviceKeyUsingSshKey(
443 DeviceKeyId.deviceKeyId(deviceId.toString()), null, username, password, sshKey));
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700444 }
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100445 }
446
447 //running an election and applying the role to a given device
448 private void runElectionFor(DeviceId deviceId) {
449 //Triggering an election for the deviceId thus only master will connect
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700450 if (!deviceId.uri().getScheme().equals(SCHEME_NAME)) {
451 // not under my scheme, skipping
David K. Bainbridge1070de92019-01-29 22:23:11 -0800452 log.debug("{} not of schema {}, skipping", deviceId, SCHEME_NAME);
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700453 return;
454 }
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100455 connectionExecutor.submit(exceptionSafe(() -> {
456 CompletableFuture<MastershipRole> role = mastershipService.requestRoleFor(deviceId);
457 try {
458 roleChanged(deviceId, role.get());
459 } catch (InterruptedException | ExecutionException e) {
460 log.error("Can't get role for {} ", deviceId, e);
461 }
462 }));
463 }
464
465 //initiating the SSh connection the a given device.
466 private void initiateConnection(DeviceId deviceId) {
467
Andrea Campanella42e7b862018-09-21 11:56:48 +0200468 if (!isReachable(deviceId)) {
469 log.warn("Can't connect to device {}", deviceId);
470 return;
471 }
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100472
473 try {
474 NetconfDevice deviceNetconf = controller.connectDevice(deviceId);
475 if (deviceNetconf != null) {
476 //storeDeviceKey(config.sshKey(), config.username(), config.password(), deviceId);
477 NetconfDeviceConfig config = cfgService.getConfig(deviceId, NetconfDeviceConfig.class);
478 //getting the device description
479 DeviceDescription deviceDescription = getDeviceDescription(deviceId, config);
480 //connecting device to ONOS
481 log.debug("Connected NETCONF device {}, on {}:{} with username {}",
482 deviceId, config.ip(), config.port(),
483 (config.path().isPresent() ? "/" + config.path().get() : ""),
484 config.username());
485 providerService.deviceConnected(deviceId, deviceDescription);
486 } else {
487 mastershipService.relinquishMastership(deviceId);
488 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(deviceId.toString()));
489 log.error("Can't connect to NETCONF device {}", deviceId);
490 }
491 } catch (Exception e) {
492 mastershipService.relinquishMastership(deviceId);
493 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(deviceId.toString()));
494 throw new IllegalStateException(new NetconfException(
495 "Can't connect to NETCONF device " + deviceId, e));
496
497 }
498
499 }
500
501 private DeviceDescription getDeviceDescription(DeviceId deviceId, NetconfDeviceConfig config) {
502 Driver driver = driverService.getDriver(deviceId);
503 if (driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
504 final DriverData data = new DefaultDriverData(driver, deviceId);
505 final DriverHandler handler = new DefaultDriverHandler(data);
506 //creating the behaviour because the core has yet no notion of device.
507 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
508 driver.createBehaviour(handler, DeviceDescriptionDiscovery.class);
509 return getDeviceRepresentation(deviceId, config, deviceDescriptionDiscovery);
510 } else {
Andrea Campanella7eb01712019-02-06 10:02:15 +0100511 return existingOrEmptyDescription(deviceId, config);
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700512 }
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700513 }
514
Andrea Campanella7eb01712019-02-06 10:02:15 +0100515 private DeviceDescription getDeviceRepresentation(DeviceId deviceId, NetconfDeviceConfig config,
516 DeviceDescriptionDiscovery deviceDescriptionDiscovery) {
517
518 DeviceDescription existingOrEmptyDescription = existingOrEmptyDescription(deviceId, config);
519 DeviceDescription newDescription = deviceDescriptionDiscovery.discoverDeviceDetails();
520 if (newDescription == null) {
521 return existingOrEmptyDescription;
522 }
523 //merging and returning
524 return new DefaultDeviceDescription(newDescription, true,
525 DefaultAnnotations.merge((DefaultAnnotations) newDescription.annotations(),
526 existingOrEmptyDescription.annotations()));
527 }
528
529 private DeviceDescription existingOrEmptyDescription(DeviceId deviceId, NetconfDeviceConfig config) {
530 Device device = deviceService.getDevice(deviceId);
531
532 if (deviceService.getDevice(deviceId) != null) {
533 //getting the previous description
534 return new DefaultDeviceDescription(device.id().uri(), device.type(),
535 device.manufacturer(), device.hwVersion(),
536 device.swVersion(), device.serialNumber(),
537 device.chassisId(), (SparseAnnotations) device.annotations());
538 }
539
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100540 ChassisId cid = new ChassisId();
541 String ipAddress = config.ip().toString();
542 DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
543 .set(IPADDRESS, ipAddress)
544 .set(PORT, String.valueOf(config.port()))
545 .set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase());
546 if (config.path().isPresent()) {
547 annotations.set(PATH, config.path().get());
548 }
549 return new DefaultDeviceDescription(deviceId.uri(), Device.Type.SWITCH,
550 UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, cid, true, annotations.build());
551 }
552
gyewan.an91d7e7e2019-01-17 15:12:48 +0900553 /**
554 * Will appropriately create connections with the device.
555 * For Master role: will create secure transport and proxy sessions.
556 * For Standby role: will create only proxy session and disconnect secure transport session.
557 * For none role: will disconnect all sessions.
558 *
559 * @param deviceId device id
560 * @param newRole new role
561 */
562 private void initiateConnection(DeviceId deviceId, MastershipRole newRole) {
563 try {
564 if (isReachable(deviceId)) {
565 NetconfDevice device = null;
566 if (newRole.equals(MastershipRole.MASTER)) {
567 device = controller.connectDevice(deviceId, true);
568 } else if (newRole.equals(MastershipRole.STANDBY)) {
569 device = controller.connectDevice(deviceId, false);
570 }
571
572 if (device != null) {
573 providerService.receivedRoleReply(deviceId, newRole, newRole);
574 } else {
575 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
576 }
577
578 }
579 } catch (Exception e) {
580 if (deviceService.getDevice(deviceId) != null) {
581 providerService.deviceDisconnected(deviceId);
582 }
583 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(deviceId.toString()));
584 throw new IllegalStateException(new NetconfException(
585 "Can't connect to NETCONF device " + deviceId, e));
586 }
587 }
588
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100589
590 private void discoverOrUpdatePorts(DeviceId deviceId) {
591 retriedPortDiscoveryMap.put(deviceId, new AtomicInteger(0));
592 AtomicInteger count = retriedPortDiscoveryMap.get(deviceId);
593 //TODO this does not enable port discovery if port changes.
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530594 Device device = deviceService.getDevice(deviceId);
595 if (device == null) {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100596 log.debug("Cant' reach device {}, not updating ports", deviceId);
Andrea Campanella105736e2017-11-23 12:52:43 +0100597 return;
598 }
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100599 if (deviceService.getPorts(deviceId).isEmpty()
600 && count != null && count.getAndIncrement() < maxRetries) {
601 if (device.is(DeviceDescriptionDiscovery.class)) {
602 providerService.updatePorts(deviceId,
603 device.as(DeviceDescriptionDiscovery.class).discoverPortDetails());
604 } else {
605 log.warn("No DeviceDescirption behaviour for device {}", deviceId);
Andrea Campanella105736e2017-11-23 12:52:43 +0100606 }
Andrea Campanella105736e2017-11-23 12:52:43 +0100607
helenyrwufd296b62016-06-22 17:43:02 -0700608 }
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100609 updatePortStatistics(device);
helenyrwufd296b62016-06-22 17:43:02 -0700610 }
611
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530612 private void updatePortStatistics(Device device) {
613 if (device.is(PortStatisticsDiscovery.class)) {
614 PortStatisticsDiscovery d = device.as(PortStatisticsDiscovery.class);
Andrea Campanellac3627842017-04-04 18:06:54 +0200615 Collection<PortStatistics> portStatistics = d.discoverPortStatistics();
616 if (portStatistics != null) {
617 providerService.updatePortStatistics(device.id(),
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100618 portStatistics);
Andrea Campanellac3627842017-04-04 18:06:54 +0200619 }
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530620 } else {
Yuta HIGUCHIe4cb8cf2017-05-01 22:18:28 -0700621 log.debug("No port statistics getter behaviour for device {}",
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100622 device.id());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700623 }
624 }
625
Andrea Campanella42e7b862018-09-21 11:56:48 +0200626 private <U> U withDeviceLock(Supplier<U> task, DeviceId deviceId) {
627 final Lock lock = deviceLocks.get(deviceId);
628 lock.lock();
629 try {
630 return task.get();
631 } finally {
632 lock.unlock();
633 }
634 }
635
636 private Runnable withDeviceLock(Runnable task, DeviceId deviceId) {
637 // Wrapper of withDeviceLock(Supplier, ...) for void tasks.
638 return () -> withDeviceLock(() -> {
639 task.run();
640 return null;
641 }, deviceId);
642 }
643
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700644 /**
645 * Listener for configuration events.
646 */
andreaeb70a942015-10-16 21:34:46 -0700647 private class InternalNetworkConfigListener implements NetworkConfigListener {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530648
andreaeb70a942015-10-16 21:34:46 -0700649 @Override
650 public void event(NetworkConfigEvent event) {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100651 if (event.configClass().equals(NetconfDeviceConfig.class) && event.config().isPresent()) {
652 connectionExecutor.execute(exceptionSafe(() ->
653 runElectionFor((DeviceId) event.config().get().subject())));
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200654 } else {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100655 log.warn("Incorrect or absent Class for Netconf Configuration");
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200656 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530657 }
658
andreaeb70a942015-10-16 21:34:46 -0700659 @Override
660 public boolean isRelevant(NetworkConfigEvent event) {
Ray Milkey02710432018-02-13 17:08:28 -0800661 return (event.configClass().equals(NetconfDeviceConfig.class)) &&
andreaeb70a942015-10-16 21:34:46 -0700662 (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
663 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530664 }
665 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700666
667 /**
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100668 * Listener for Netconf Controller Events.
669 */
670 private class InnerNetconfDeviceListener implements NetconfDeviceListener {
671
672 @Override
673 public void deviceAdded(DeviceId deviceId) {
674 //no-op
675 log.debug("Netconf device {} added to Netconf controller", deviceId);
676 }
677
678 @Override
679 public void deviceRemoved(DeviceId deviceId) {
680 Preconditions.checkNotNull(deviceId, ISNULL);
681
682 if (deviceService.getDevice(deviceId) != null) {
683 providerService.deviceDisconnected(deviceId);
684 retriedPortDiscoveryMap.remove(deviceId);
685 log.debug("Netconf device {} removed from Netconf controller", deviceId);
686 } else {
687 log.warn("Netconf device {} does not exist in the store, " +
688 "it may already have been removed", deviceId);
689 }
690 }
691 }
692
693 /**
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700694 * Listener for core device events.
695 */
696 private class InternalDeviceListener implements DeviceListener {
697 @Override
698 public void event(DeviceEvent event) {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100699 connectionExecutor.submit(exceptionSafe(() -> discoverOrUpdatePorts(event.subject().id())));
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700700 }
701
702 @Override
703 public boolean isRelevant(DeviceEvent event) {
Andrea Campanella42e7b862018-09-21 11:56:48 +0200704 if (event.type() != DeviceEvent.Type.DEVICE_ADDED &&
705 event.type() != DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED) {
Thomas Vachuska5b38dc02018-05-10 15:24:40 -0700706 return false;
707 }
Andrea Campanella968f93f2017-06-08 11:09:28 +0200708 return (SCHEME_NAME.equalsIgnoreCase(event.subject().annotations().value(AnnotationKeys.PROTOCOL)) ||
709 (SCHEME_NAME.equalsIgnoreCase(event.subject().id().uri().getScheme()))) &&
Michele Santuari576f09c2016-09-28 14:20:00 +0200710 mastershipService.isLocalMaster(event.subject().id());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700711 }
712 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530713}