blob: fe6060d35aee6f8b56021bb08886026d992af306 [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 = {
Andrea Campanella02f86c82019-03-18 16:57:27 -0700112 POLL_FREQUENCY_SECONDS + ":Integer=" + POLL_FREQUENCY_SECONDS_DEFAULT,
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700113 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);
DongRyeol Cha40259002019-03-15 18:26:11 +0900208 scheduledTask = schedulePolling();
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700209 modified(context);
Thomas Vachuskad6811712015-04-29 21:37:04 -0700210 log.info("Started");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530211 }
212
andreaeb70a942015-10-16 21:34:46 -0700213
Sanjay Se8dcfee2015-04-23 10:07:08 +0530214 @Deactivate
andreaeb70a942015-10-16 21:34:46 -0700215 public void deactivate() {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100216 cfgService.removeListener(cfgListener);
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700217 componentConfigService.unregisterProperties(getClass(), false);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700218 deviceService.removeListener(deviceListener);
219 active = false;
220 controller.getNetconfDevices().forEach(id -> {
221 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(id.toString()));
222 controller.disconnectDevice(id, true);
223 });
Andrea Campanella86294db2016-03-07 11:42:49 -0800224 controller.removeDeviceListener(innerNodeListener);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530225 providerRegistry.unregister(this);
226 providerService = null;
mskala832d0472017-06-09 16:31:42 +0200227 retriedPortDiscoveryMap.clear();
Ray Milkey02710432018-02-13 17:08:28 -0800228 cfgService.unregisterConfigFactory(factory);
helenyrwufd296b62016-06-22 17:43:02 -0700229 scheduledTask.cancel(true);
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100230 connectionExecutor.shutdown();
231 pollingExecutor.shutdown();
Sanjay Seb5eebb2015-04-24 15:44:50 +0530232 log.info("Stopped");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530233 }
234
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700235
236 @Modified
237 public void modified(ComponentContext context) {
238 if (context != null) {
239 Dictionary<?, ?> properties = context.getProperties();
DongRyeol Cha40259002019-03-15 18:26:11 +0900240 int newPollFrequency = Tools.getIntegerProperty(properties, POLL_FREQUENCY_SECONDS,
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100241 POLL_FREQUENCY_SECONDS_DEFAULT);
DongRyeol Cha40259002019-03-15 18:26:11 +0900242
243 if (newPollFrequency != pollFrequency) {
244 pollFrequency = newPollFrequency;
245
246 if (scheduledTask != null) {
247 scheduledTask.cancel(false);
248 }
249 scheduledTask = schedulePolling();
250 log.info("Configured. Poll frequency is configured to {} seconds", pollFrequency);
251 }
mskala832d0472017-06-09 16:31:42 +0200252
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700253 maxRetries = Tools.getIntegerProperty(properties, MAX_RETRIES, MAX_RETRIES_DEFAULT);
mskala832d0472017-06-09 16:31:42 +0200254 log.info("Configured. Number of retries is configured to {} times", maxRetries);
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700255 }
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700256 }
257
andreaeb70a942015-10-16 21:34:46 -0700258 public NetconfDeviceProvider() {
Andrea Campanella101417d2015-12-11 17:58:07 -0800259 super(new ProviderId(SCHEME_NAME, DEVICE_PROVIDER_PACKAGE));
Sanjay Se8dcfee2015-04-23 10:07:08 +0530260 }
261
262 @Override
263 public void triggerProbe(DeviceId deviceId) {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100264 //Not implemented, unused in netconf cases.
265 log.debug("Probing {} not implemented, not useful for NETCONF", deviceId);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530266 }
267
268 @Override
269 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100270 log.debug("Request role change {}, {}", deviceId, newRole);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700271 if (active) {
272 switch (newRole) {
273 case MASTER:
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100274 if (controller.getNetconfDevice(deviceId) == null) {
275 connectionExecutor.execute(exceptionSafe(() -> withDeviceLock(
276 () -> initiateConnection(deviceId), deviceId).run()));
277 log.debug("Accepting mastership role change to {} for device {}", newRole, deviceId);
278 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700279 break;
280 case STANDBY:
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100281 //TODO this issue a warning on the first election/connection
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700282 controller.disconnectDevice(deviceId, false);
gyewan.an91d7e7e2019-01-17 15:12:48 +0900283 withDeviceLock(
284 () -> initiateConnection(deviceId, newRole), deviceId).run();
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700285 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.STANDBY);
286 //else no-op
287 break;
288 case NONE:
289 controller.disconnectDevice(deviceId, false);
290 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
291 break;
292 default:
293 log.error("Unimplemented Mastership state : {}", newRole);
294
295 }
296 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530297 }
298
299 @Override
300 public boolean isReachable(DeviceId deviceId) {
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700301 boolean sessionExists =
302 Optional.ofNullable(controller.getDevicesMap().get(deviceId))
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100303 .map(NetconfDevice::isActive)
304 .orElse(false);
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700305 if (sessionExists) {
306 return true;
307 }
308
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700309 //FIXME this is a workaround util device state is shared
310 // between controller instances.
311 Device device = deviceService.getDevice(deviceId);
312 String ip;
313 int port;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700314 if (device != null) {
315 ip = device.annotations().value(IPADDRESS);
316 port = Integer.parseInt(device.annotations().value(PORT));
317 } else {
David K. Bainbridge56e90232018-12-18 23:25:08 -0800318 Triple<String, Integer, Optional<String>> info = extractIpPortPath(deviceId);
319 ip = info.getLeft();
320 port = info.getMiddle();
Sanjay Se8dcfee2015-04-23 10:07:08 +0530321 }
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700322 // FIXME just opening TCP session probably is not the appropriate
323 // method to test reachability.
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700324 //test connection to device opening a socket to it.
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700325 log.debug("Testing reachability for {}:{}", ip, port);
Andrea Campanella42e7b862018-09-21 11:56:48 +0200326 Socket socket = new Socket();
327 try {
328 socket.connect(new InetSocketAddress(ip, port), 1000);
Yuta HIGUCHI0454d702017-03-17 10:08:38 -0700329 log.debug("rechability of {}, {}, {}", deviceId, socket.isConnected(), !socket.isClosed());
Andrea Campanella42e7b862018-09-21 11:56:48 +0200330 boolean isConnected = socket.isConnected() && !socket.isClosed();
331 socket.close();
332 return isConnected;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700333 } catch (IOException e) {
334 log.info("Device {} is not reachable", deviceId);
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700335 log.debug(" error details", e);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700336 return false;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700337 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530338 }
339
Saurav Dasa2d37502016-03-25 17:50:40 -0700340 @Override
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100341 public void changePortState(DeviceId deviceId, PortNumber portNumber, boolean enable) {
Andrea Campanella32813682017-10-23 15:29:24 +0200342 Device device = deviceService.getDevice(deviceId);
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100343 if (device == null) {
344 log.error("Device {} is not present in the store", deviceId);
345 return;
Andrea Campanella32813682017-10-23 15:29:24 +0200346 }
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100347 if (!mastershipService.isLocalMaster(deviceId)) {
348 log.info("Not master but {}, not changing port state", mastershipService.getLocalRole(deviceId));
349 return;
350 }
351 if (!device.is(PortAdmin.class)) {
352 log.warn("Device {} does not support Port Admin", deviceId);
353 return;
354 }
355 PortAdmin portAdmin = device.as(PortAdmin.class);
356 CompletableFuture<Boolean> modified;
357 if (enable) {
358 modified = portAdmin.enable(portNumber);
359 } else {
360 modified = portAdmin.disable(portNumber);
361 }
362 modified.thenAccept(result -> {
363 if (result) {
364 Port port = deviceService.getPort(deviceId, portNumber);
365 //rebuilding port description with admin state changed.
366 providerService.portStatusChanged(deviceId,
367 DefaultPortDescription.builder()
368 .withPortNumber(portNumber)
369 .isEnabled(enable)
370 .isRemoved(false)
371 .type(port.type())
372 .portSpeed(port.portSpeed())
373 .annotations((SparseAnnotations) port.annotations())
374 .build());
375 } else {
376 log.warn("Your device {} port {} status can't be changed to {}",
377 deviceId, portNumber, enable);
378 }
379 });
Saurav Dasa2d37502016-03-25 17:50:40 -0700380 }
381
Thomas Vachuska5b38dc02018-05-10 15:24:40 -0700382 @Override
383 public void triggerDisconnect(DeviceId deviceId) {
384 log.debug("Forcing disconnect for device {}", deviceId);
385 controller.disconnectDevice(deviceId, true);
386 }
387
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100388 private ScheduledFuture schedulePolling() {
389 return pollingExecutor.scheduleAtFixedRate(exceptionSafe(this::checkAndUpdateDevices),
390 pollFrequency / 10,
391 pollFrequency, TimeUnit.SECONDS);
andreaeb70a942015-10-16 21:34:46 -0700392 }
393
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100394 private Runnable exceptionSafe(Runnable runnable) {
395 return () -> {
396 try {
397 runnable.run();
398 } catch (Exception e) {
399 log.error("Unhandled Exception", e);
400 }
401 };
402 }
403
404 //Connecting devices with initial config
andreaeb70a942015-10-16 21:34:46 -0700405 private void connectDevices() {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100406 Set<DeviceId> deviceSubjects = cfgService.getSubjects(DeviceId.class, NetconfDeviceConfig.class);
DongRyeol Cha07f0d372019-04-03 12:29:00 +0900407 deviceSubjects.parallelStream().forEach(deviceId -> {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100408 connectionExecutor.execute(exceptionSafe(() -> runElectionFor(deviceId)));
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200409 });
helenyrwufd296b62016-06-22 17:43:02 -0700410 }
411
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100412 //updating keys and device info
413 private void checkAndUpdateDevices() {
414 Set<DeviceId> deviceSubjects = cfgService.getSubjects(DeviceId.class, NetconfDeviceConfig.class);
DongRyeol Cha07f0d372019-04-03 12:29:00 +0900415 deviceSubjects.parallelStream().forEach(deviceId -> {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100416 log.debug("check and update {}", deviceId);
417 NetconfDeviceConfig config = cfgService.getConfig(deviceId, NetconfDeviceConfig.class);
418 storeDeviceKey(config.sshKey(), config.username(), config.password(), deviceId);
419 discoverOrUpdatePorts(deviceId);
420 });
421 }
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700422
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100423 //Saving device keys in the store
424 private void storeDeviceKey(String sshKey, String username, String password, DeviceId deviceId) {
425 if (sshKey.equals("")) {
426 deviceKeyAdminService.addKey(
427 DeviceKey.createDeviceKeyUsingUsernamePassword(
428 DeviceKeyId.deviceKeyId(deviceId.toString()), null, username, password));
429 } else {
430 deviceKeyAdminService.addKey(
431 DeviceKey.createDeviceKeyUsingSshKey(
432 DeviceKeyId.deviceKeyId(deviceId.toString()), null, username, password, sshKey));
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700433 }
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100434 }
435
436 //running an election and applying the role to a given device
437 private void runElectionFor(DeviceId deviceId) {
438 //Triggering an election for the deviceId thus only master will connect
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700439 if (!deviceId.uri().getScheme().equals(SCHEME_NAME)) {
440 // not under my scheme, skipping
David K. Bainbridge1070de92019-01-29 22:23:11 -0800441 log.debug("{} not of schema {}, skipping", deviceId, SCHEME_NAME);
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700442 return;
443 }
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100444 connectionExecutor.submit(exceptionSafe(() -> {
445 CompletableFuture<MastershipRole> role = mastershipService.requestRoleFor(deviceId);
446 try {
447 roleChanged(deviceId, role.get());
448 } catch (InterruptedException | ExecutionException e) {
449 log.error("Can't get role for {} ", deviceId, e);
450 }
451 }));
452 }
453
454 //initiating the SSh connection the a given device.
455 private void initiateConnection(DeviceId deviceId) {
456
Andrea Campanella42e7b862018-09-21 11:56:48 +0200457 if (!isReachable(deviceId)) {
458 log.warn("Can't connect to device {}", deviceId);
459 return;
460 }
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100461
462 try {
463 NetconfDevice deviceNetconf = controller.connectDevice(deviceId);
464 if (deviceNetconf != null) {
465 //storeDeviceKey(config.sshKey(), config.username(), config.password(), deviceId);
466 NetconfDeviceConfig config = cfgService.getConfig(deviceId, NetconfDeviceConfig.class);
467 //getting the device description
468 DeviceDescription deviceDescription = getDeviceDescription(deviceId, config);
469 //connecting device to ONOS
470 log.debug("Connected NETCONF device {}, on {}:{} with username {}",
471 deviceId, config.ip(), config.port(),
472 (config.path().isPresent() ? "/" + config.path().get() : ""),
473 config.username());
474 providerService.deviceConnected(deviceId, deviceDescription);
475 } else {
476 mastershipService.relinquishMastership(deviceId);
477 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(deviceId.toString()));
478 log.error("Can't connect to NETCONF device {}", deviceId);
479 }
480 } catch (Exception e) {
481 mastershipService.relinquishMastership(deviceId);
482 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(deviceId.toString()));
483 throw new IllegalStateException(new NetconfException(
484 "Can't connect to NETCONF device " + deviceId, e));
485
486 }
487
488 }
489
490 private DeviceDescription getDeviceDescription(DeviceId deviceId, NetconfDeviceConfig config) {
491 Driver driver = driverService.getDriver(deviceId);
492 if (driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
493 final DriverData data = new DefaultDriverData(driver, deviceId);
494 final DriverHandler handler = new DefaultDriverHandler(data);
495 //creating the behaviour because the core has yet no notion of device.
496 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
497 driver.createBehaviour(handler, DeviceDescriptionDiscovery.class);
498 return getDeviceRepresentation(deviceId, config, deviceDescriptionDiscovery);
499 } else {
Andrea Campanella7eb01712019-02-06 10:02:15 +0100500 return existingOrEmptyDescription(deviceId, config);
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700501 }
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700502 }
503
Andrea Campanella7eb01712019-02-06 10:02:15 +0100504 private DeviceDescription getDeviceRepresentation(DeviceId deviceId, NetconfDeviceConfig config,
505 DeviceDescriptionDiscovery deviceDescriptionDiscovery) {
506
507 DeviceDescription existingOrEmptyDescription = existingOrEmptyDescription(deviceId, config);
508 DeviceDescription newDescription = deviceDescriptionDiscovery.discoverDeviceDetails();
509 if (newDescription == null) {
510 return existingOrEmptyDescription;
511 }
512 //merging and returning
513 return new DefaultDeviceDescription(newDescription, true,
514 DefaultAnnotations.merge((DefaultAnnotations) newDescription.annotations(),
515 existingOrEmptyDescription.annotations()));
516 }
517
518 private DeviceDescription existingOrEmptyDescription(DeviceId deviceId, NetconfDeviceConfig config) {
519 Device device = deviceService.getDevice(deviceId);
520
521 if (deviceService.getDevice(deviceId) != null) {
522 //getting the previous description
523 return new DefaultDeviceDescription(device.id().uri(), device.type(),
524 device.manufacturer(), device.hwVersion(),
525 device.swVersion(), device.serialNumber(),
526 device.chassisId(), (SparseAnnotations) device.annotations());
527 }
528
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100529 ChassisId cid = new ChassisId();
530 String ipAddress = config.ip().toString();
531 DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
532 .set(IPADDRESS, ipAddress)
533 .set(PORT, String.valueOf(config.port()))
534 .set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase());
535 if (config.path().isPresent()) {
536 annotations.set(PATH, config.path().get());
537 }
538 return new DefaultDeviceDescription(deviceId.uri(), Device.Type.SWITCH,
539 UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, cid, true, annotations.build());
540 }
541
gyewan.an91d7e7e2019-01-17 15:12:48 +0900542 /**
543 * Will appropriately create connections with the device.
544 * For Master role: will create secure transport and proxy sessions.
545 * For Standby role: will create only proxy session and disconnect secure transport session.
546 * For none role: will disconnect all sessions.
547 *
548 * @param deviceId device id
549 * @param newRole new role
550 */
551 private void initiateConnection(DeviceId deviceId, MastershipRole newRole) {
552 try {
553 if (isReachable(deviceId)) {
554 NetconfDevice device = null;
555 if (newRole.equals(MastershipRole.MASTER)) {
556 device = controller.connectDevice(deviceId, true);
557 } else if (newRole.equals(MastershipRole.STANDBY)) {
558 device = controller.connectDevice(deviceId, false);
559 }
560
561 if (device != null) {
562 providerService.receivedRoleReply(deviceId, newRole, newRole);
563 } else {
564 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
565 }
566
567 }
568 } catch (Exception e) {
569 if (deviceService.getDevice(deviceId) != null) {
570 providerService.deviceDisconnected(deviceId);
571 }
572 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(deviceId.toString()));
573 throw new IllegalStateException(new NetconfException(
574 "Can't connect to NETCONF device " + deviceId, e));
575 }
576 }
577
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100578
579 private void discoverOrUpdatePorts(DeviceId deviceId) {
580 retriedPortDiscoveryMap.put(deviceId, new AtomicInteger(0));
581 AtomicInteger count = retriedPortDiscoveryMap.get(deviceId);
582 //TODO this does not enable port discovery if port changes.
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530583 Device device = deviceService.getDevice(deviceId);
584 if (device == null) {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100585 log.debug("Cant' reach device {}, not updating ports", deviceId);
Andrea Campanella105736e2017-11-23 12:52:43 +0100586 return;
587 }
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100588 if (deviceService.getPorts(deviceId).isEmpty()
589 && count != null && count.getAndIncrement() < maxRetries) {
590 if (device.is(DeviceDescriptionDiscovery.class)) {
591 providerService.updatePorts(deviceId,
592 device.as(DeviceDescriptionDiscovery.class).discoverPortDetails());
593 } else {
594 log.warn("No DeviceDescirption behaviour for device {}", deviceId);
Andrea Campanella105736e2017-11-23 12:52:43 +0100595 }
Andrea Campanella105736e2017-11-23 12:52:43 +0100596
helenyrwufd296b62016-06-22 17:43:02 -0700597 }
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100598 updatePortStatistics(device);
helenyrwufd296b62016-06-22 17:43:02 -0700599 }
600
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530601 private void updatePortStatistics(Device device) {
602 if (device.is(PortStatisticsDiscovery.class)) {
603 PortStatisticsDiscovery d = device.as(PortStatisticsDiscovery.class);
Andrea Campanellac3627842017-04-04 18:06:54 +0200604 Collection<PortStatistics> portStatistics = d.discoverPortStatistics();
605 if (portStatistics != null) {
606 providerService.updatePortStatistics(device.id(),
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100607 portStatistics);
Andrea Campanellac3627842017-04-04 18:06:54 +0200608 }
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530609 } else {
Yuta HIGUCHIe4cb8cf2017-05-01 22:18:28 -0700610 log.debug("No port statistics getter behaviour for device {}",
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100611 device.id());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700612 }
613 }
614
Andrea Campanella42e7b862018-09-21 11:56:48 +0200615 private <U> U withDeviceLock(Supplier<U> task, DeviceId deviceId) {
616 final Lock lock = deviceLocks.get(deviceId);
617 lock.lock();
618 try {
619 return task.get();
620 } finally {
621 lock.unlock();
622 }
623 }
624
625 private Runnable withDeviceLock(Runnable task, DeviceId deviceId) {
626 // Wrapper of withDeviceLock(Supplier, ...) for void tasks.
627 return () -> withDeviceLock(() -> {
628 task.run();
629 return null;
630 }, deviceId);
631 }
632
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700633 /**
634 * Listener for configuration events.
635 */
andreaeb70a942015-10-16 21:34:46 -0700636 private class InternalNetworkConfigListener implements NetworkConfigListener {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530637
andreaeb70a942015-10-16 21:34:46 -0700638 @Override
639 public void event(NetworkConfigEvent event) {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100640 if (event.configClass().equals(NetconfDeviceConfig.class) && event.config().isPresent()) {
641 connectionExecutor.execute(exceptionSafe(() ->
642 runElectionFor((DeviceId) event.config().get().subject())));
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200643 } else {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100644 log.warn("Incorrect or absent Class for Netconf Configuration");
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200645 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530646 }
647
andreaeb70a942015-10-16 21:34:46 -0700648 @Override
649 public boolean isRelevant(NetworkConfigEvent event) {
Ray Milkey02710432018-02-13 17:08:28 -0800650 return (event.configClass().equals(NetconfDeviceConfig.class)) &&
andreaeb70a942015-10-16 21:34:46 -0700651 (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
652 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530653 }
654 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700655
656 /**
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100657 * Listener for Netconf Controller Events.
658 */
659 private class InnerNetconfDeviceListener implements NetconfDeviceListener {
660
661 @Override
662 public void deviceAdded(DeviceId deviceId) {
663 //no-op
664 log.debug("Netconf device {} added to Netconf controller", deviceId);
665 }
666
667 @Override
668 public void deviceRemoved(DeviceId deviceId) {
669 Preconditions.checkNotNull(deviceId, ISNULL);
670
671 if (deviceService.getDevice(deviceId) != null) {
672 providerService.deviceDisconnected(deviceId);
673 retriedPortDiscoveryMap.remove(deviceId);
674 log.debug("Netconf device {} removed from Netconf controller", deviceId);
675 } else {
676 log.warn("Netconf device {} does not exist in the store, " +
677 "it may already have been removed", deviceId);
678 }
679 }
680 }
681
682 /**
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700683 * Listener for core device events.
684 */
685 private class InternalDeviceListener implements DeviceListener {
686 @Override
687 public void event(DeviceEvent event) {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100688 connectionExecutor.submit(exceptionSafe(() -> discoverOrUpdatePorts(event.subject().id())));
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700689 }
690
691 @Override
692 public boolean isRelevant(DeviceEvent event) {
Andrea Campanella42e7b862018-09-21 11:56:48 +0200693 if (event.type() != DeviceEvent.Type.DEVICE_ADDED &&
694 event.type() != DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED) {
Thomas Vachuska5b38dc02018-05-10 15:24:40 -0700695 return false;
696 }
Andrea Campanella968f93f2017-06-08 11:09:28 +0200697 return (SCHEME_NAME.equalsIgnoreCase(event.subject().annotations().value(AnnotationKeys.PROTOCOL)) ||
698 (SCHEME_NAME.equalsIgnoreCase(event.subject().id().uri().getScheme()))) &&
Michele Santuari576f09c2016-09-28 14:20:00 +0200699 mastershipService.isLocalMaster(event.subject().id());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700700 }
701 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530702}