blob: 80b9108f3826eff75a7da18b678f584bb556e8f7 [file] [log] [blame]
Sanjay Se8dcfee2015-04-23 10:07:08 +05301/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Sanjay Se8dcfee2015-04-23 10:07:08 +05303 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
andreaeb70a942015-10-16 21:34:46 -070016
Sanjay Se8dcfee2015-04-23 10:07:08 +053017package org.onosproject.provider.netconf.device.impl;
18
andreaeb70a942015-10-16 21:34:46 -070019import com.google.common.base.Preconditions;
David K. Bainbridge56e90232018-12-18 23:25:08 -080020import org.apache.commons.lang3.tuple.Triple;
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +010021import com.google.common.util.concurrent.Striped;
Sanjay Se8dcfee2015-04-23 10:07:08 +053022import org.onlab.packet.ChassisId;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -070023import org.onlab.util.Tools;
24import org.onosproject.cfg.ComponentConfigService;
andreaeb70a942015-10-16 21:34:46 -070025import org.onosproject.core.CoreService;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070026import org.onosproject.mastership.MastershipService;
Marc De Leenheerb0d131c2016-03-01 20:34:59 -080027import org.onosproject.net.AnnotationKeys;
andreaeb70a942015-10-16 21:34:46 -070028import org.onosproject.net.DefaultAnnotations;
Sanjay Se8dcfee2015-04-23 10:07:08 +053029import org.onosproject.net.Device;
30import org.onosproject.net.DeviceId;
31import org.onosproject.net.MastershipRole;
Ray Milkey02710432018-02-13 17:08:28 -080032import org.onosproject.net.Port;
Saurav Dasa2d37502016-03-25 17:50:40 -070033import org.onosproject.net.PortNumber;
andreaeb70a942015-10-16 21:34:46 -070034import org.onosproject.net.SparseAnnotations;
Ray Milkey02710432018-02-13 17:08:28 -080035import org.onosproject.net.behaviour.PortAdmin;
andreaeb70a942015-10-16 21:34:46 -070036import org.onosproject.net.config.ConfigFactory;
37import org.onosproject.net.config.NetworkConfigEvent;
38import org.onosproject.net.config.NetworkConfigListener;
39import org.onosproject.net.config.NetworkConfigRegistry;
Andrea Campanella34cf65c2017-04-12 13:51:32 +020040import org.onosproject.net.config.basics.SubjectFactories;
Sanjay Se8dcfee2015-04-23 10:07:08 +053041import org.onosproject.net.device.DefaultDeviceDescription;
Andrea Campanella32813682017-10-23 15:29:24 +020042import org.onosproject.net.device.DefaultPortDescription;
Sanjay Se8dcfee2015-04-23 10:07:08 +053043import org.onosproject.net.device.DeviceDescription;
Andrea Campanella6c71a052016-04-22 11:56:31 -070044import org.onosproject.net.device.DeviceDescriptionDiscovery;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070045import org.onosproject.net.device.DeviceEvent;
46import org.onosproject.net.device.DeviceListener;
Sanjay Se8dcfee2015-04-23 10:07:08 +053047import org.onosproject.net.device.DeviceProvider;
48import org.onosproject.net.device.DeviceProviderRegistry;
49import org.onosproject.net.device.DeviceProviderService;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -080050import org.onosproject.net.device.DeviceService;
Andrea Campanellac3627842017-04-04 18:06:54 +020051import org.onosproject.net.device.PortStatistics;
Gaurav Agrawaldab4d772017-03-29 15:15:13 +053052import org.onosproject.net.device.PortStatisticsDiscovery;
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +010053import org.onosproject.net.driver.DefaultDriverData;
54import org.onosproject.net.driver.DefaultDriverHandler;
55import org.onosproject.net.driver.Driver;
56import org.onosproject.net.driver.DriverData;
57import org.onosproject.net.driver.DriverHandler;
58import org.onosproject.net.driver.DriverService;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070059import org.onosproject.net.key.DeviceKey;
60import org.onosproject.net.key.DeviceKeyAdminService;
61import org.onosproject.net.key.DeviceKeyId;
Sanjay Se8dcfee2015-04-23 10:07:08 +053062import org.onosproject.net.provider.AbstractProvider;
63import org.onosproject.net.provider.ProviderId;
andreaeb70a942015-10-16 21:34:46 -070064import org.onosproject.netconf.NetconfController;
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -070065import org.onosproject.netconf.NetconfDevice;
andreaeb70a942015-10-16 21:34:46 -070066import org.onosproject.netconf.NetconfDeviceListener;
Andrea Campanella8b1cb672016-01-25 13:58:58 -080067import org.onosproject.netconf.NetconfException;
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -070068import org.onosproject.netconf.config.NetconfDeviceConfig;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -070069import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070070import org.osgi.service.component.annotations.Activate;
71import org.osgi.service.component.annotations.Component;
72import org.osgi.service.component.annotations.Deactivate;
73import org.osgi.service.component.annotations.Modified;
74import org.osgi.service.component.annotations.Reference;
75import org.osgi.service.component.annotations.ReferenceCardinality;
Sanjay Se8dcfee2015-04-23 10:07:08 +053076import org.slf4j.Logger;
77
Andrea Campanella087ceb92015-12-07 09:58:34 -080078import java.io.IOException;
Andrea Campanella42e7b862018-09-21 11:56:48 +020079import java.net.InetSocketAddress;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070080import java.net.Socket;
Andrea Campanellac3627842017-04-04 18:06:54 +020081import java.util.Collection;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -070082import java.util.Dictionary;
mskala832d0472017-06-09 16:31:42 +020083import java.util.Map;
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -070084import java.util.Optional;
Andrea Campanella34cf65c2017-04-12 13:51:32 +020085import java.util.Set;
Andrea Campanella32813682017-10-23 15:29:24 +020086import java.util.concurrent.CompletableFuture;
mskala832d0472017-06-09 16:31:42 +020087import java.util.concurrent.ConcurrentHashMap;
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +010088import java.util.concurrent.ExecutionException;
Andrea Campanella5c999e22016-03-01 15:12:53 -080089import java.util.concurrent.ExecutorService;
90import java.util.concurrent.Executors;
helenyrwufd296b62016-06-22 17:43:02 -070091import java.util.concurrent.ScheduledExecutorService;
92import java.util.concurrent.ScheduledFuture;
93import java.util.concurrent.TimeUnit;
mskala832d0472017-06-09 16:31:42 +020094import java.util.concurrent.atomic.AtomicInteger;
Andrea Campanella42e7b862018-09-21 11:56:48 +020095import java.util.concurrent.locks.Lock;
96import java.util.function.Supplier;
andreaeb70a942015-10-16 21:34:46 -070097
Yuta HIGUCHI1624df12016-07-21 16:54:33 -070098import static java.util.concurrent.Executors.newScheduledThreadPool;
Andrea Campanella5c999e22016-03-01 15:12:53 -080099import static org.onlab.util.Tools.groupedThreads;
David K. Bainbridge56e90232018-12-18 23:25:08 -0800100import static org.onosproject.netconf.NetconfDeviceInfo.extractIpPortPath;
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100101import static org.onosproject.provider.netconf.device.impl.OsgiPropertyConstants.MAX_RETRIES;
102import static org.onosproject.provider.netconf.device.impl.OsgiPropertyConstants.MAX_RETRIES_DEFAULT;
103import static org.onosproject.provider.netconf.device.impl.OsgiPropertyConstants.POLL_FREQUENCY_SECONDS;
104import static org.onosproject.provider.netconf.device.impl.OsgiPropertyConstants.POLL_FREQUENCY_SECONDS_DEFAULT;
andreaeb70a942015-10-16 21:34:46 -0700105import static org.slf4j.LoggerFactory.getLogger;
106
Sanjay Se8dcfee2015-04-23 10:07:08 +0530107/**
andreaeb70a942015-10-16 21:34:46 -0700108 * Provider which uses an NETCONF controller to detect device.
Sanjay Se8dcfee2015-04-23 10:07:08 +0530109 */
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700110@Component(immediate = true,
111 property = {
112 POLL_FREQUENCY_SECONDS_DEFAULT + ":Integer=" + POLL_FREQUENCY_SECONDS_DEFAULT,
113 MAX_RETRIES + ":Integer=" + MAX_RETRIES_DEFAULT,
114 })
Sanjay Se8dcfee2015-04-23 10:07:08 +0530115public class NetconfDeviceProvider extends AbstractProvider
116 implements DeviceProvider {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700117
andreaeb70a942015-10-16 21:34:46 -0700118 private final Logger log = getLogger(getClass());
Sanjay Se8dcfee2015-04-23 10:07:08 +0530119
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700120 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Sanjay Se8dcfee2015-04-23 10:07:08 +0530121 protected DeviceProviderRegistry providerRegistry;
122
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700123 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella101417d2015-12-11 17:58:07 -0800124 protected NetconfController controller;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530125
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700126 @Reference(cardinality = ReferenceCardinality.MANDATORY)
andreaeb70a942015-10-16 21:34:46 -0700127 protected NetworkConfigRegistry cfgService;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530128
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700129 @Reference(cardinality = ReferenceCardinality.MANDATORY)
andreaeb70a942015-10-16 21:34:46 -0700130 protected CoreService coreService;
Thomas Vachuskad6811712015-04-29 21:37:04 -0700131
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700132 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700133 protected DeviceService deviceService;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -0800134
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700135 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100136 protected DriverService driverService;
137
138 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700139 protected DeviceKeyAdminService deviceKeyAdminService;
140
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700141 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700142 protected MastershipService mastershipService;
143
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700144 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700145 protected ComponentConfigService componentConfigService;
146
147
Michele Santuari576f09c2016-09-28 14:20:00 +0200148 protected static final String APP_NAME = "org.onosproject.netconf";
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200149 protected static final String SCHEME_NAME = "netconf";
Andrea Campanella101417d2015-12-11 17:58:07 -0800150 private static final String DEVICE_PROVIDER_PACKAGE = "org.onosproject.netconf.provider.device";
151 private static final String UNKNOWN = "unknown";
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700152 protected static final String ISNULL = "NetconfDeviceInfo is null";
153 private static final String IPADDRESS = "ipaddress";
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700154 private static final String PORT = "port";
David K. Bainbridge56e90232018-12-18 23:25:08 -0800155 private static final String PATH = "path";
helenyrwufd296b62016-06-22 17:43:02 -0700156 private static final int CORE_POOL_SIZE = 10;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700157
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100158 /**
159 * Configure poll frequency for port status and statistics; default is 30 sec.
160 */
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700161 private int pollFrequency = POLL_FREQUENCY_SECONDS_DEFAULT;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530162
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100163 /**
164 * Configure maximum allowed number of retries for obtaining list of ports; default is 5 times.
165 */
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700166 private int maxRetries = MAX_RETRIES_DEFAULT;
mskala832d0472017-06-09 16:31:42 +0200167
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100168 protected ExecutorService connectionExecutor = Executors.newFixedThreadPool(CORE_POOL_SIZE,
169 groupedThreads("onos/netconfDeviceProviderConnection",
170 "connection-executor-%d", log));
171 protected ScheduledExecutorService pollingExecutor = newScheduledThreadPool(CORE_POOL_SIZE,
172 groupedThreads("onos/netconfDeviceProviderPoll",
173 "polling-executor-%d", log));
Andrea Campanella5c999e22016-03-01 15:12:53 -0800174
Michele Santuari576f09c2016-09-28 14:20:00 +0200175 protected DeviceProviderService providerService;
mskala832d0472017-06-09 16:31:42 +0200176 private final Map<DeviceId, AtomicInteger> retriedPortDiscoveryMap = new ConcurrentHashMap<>();
Michele Santuari576f09c2016-09-28 14:20:00 +0200177 protected ScheduledFuture<?> scheduledTask;
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100178 private final Striped<Lock> deviceLocks = Striped.lock(30);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530179
Ray Milkey02710432018-02-13 17:08:28 -0800180 protected final ConfigFactory factory =
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700181 // TODO consider moving Config registration to NETCONF ctl bundle
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200182 new ConfigFactory<DeviceId, NetconfDeviceConfig>(
183 SubjectFactories.DEVICE_SUBJECT_FACTORY,
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700184 NetconfDeviceConfig.class, NetconfDeviceConfig.CONFIG_KEY) {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200185 @Override
186 public NetconfDeviceConfig createConfig() {
187 return new NetconfDeviceConfig();
188 }
Ray Milkey02710432018-02-13 17:08:28 -0800189 };
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200190
Michele Santuari576f09c2016-09-28 14:20:00 +0200191 protected final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100192 private NetconfDeviceListener innerNodeListener = new InnerNetconfDeviceListener();
193 private InternalDeviceListener deviceListener = new InternalDeviceListener();
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700194 private boolean active;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530195
Sanjay Se8dcfee2015-04-23 10:07:08 +0530196
197 @Activate
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700198 public void activate(ComponentContext context) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700199 active = true;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700200 componentConfigService.registerProperties(getClass());
Sanjay Se8dcfee2015-04-23 10:07:08 +0530201 providerService = providerRegistry.register(this);
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100202 coreService.registerApplication(APP_NAME);
Ray Milkey02710432018-02-13 17:08:28 -0800203 cfgService.registerConfigFactory(factory);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700204 cfgService.addListener(cfgListener);
andreaeb70a942015-10-16 21:34:46 -0700205 controller.addDeviceListener(innerNodeListener);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700206 deviceService.addListener(deviceListener);
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100207 pollingExecutor.execute(NetconfDeviceProvider.this::connectDevices);
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700208 modified(context);
Thomas Vachuskad6811712015-04-29 21:37:04 -0700209 log.info("Started");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530210 }
211
andreaeb70a942015-10-16 21:34:46 -0700212
Sanjay Se8dcfee2015-04-23 10:07:08 +0530213 @Deactivate
andreaeb70a942015-10-16 21:34:46 -0700214 public void deactivate() {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100215 cfgService.removeListener(cfgListener);
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700216 componentConfigService.unregisterProperties(getClass(), false);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700217 deviceService.removeListener(deviceListener);
218 active = false;
219 controller.getNetconfDevices().forEach(id -> {
220 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(id.toString()));
221 controller.disconnectDevice(id, true);
222 });
Andrea Campanella86294db2016-03-07 11:42:49 -0800223 controller.removeDeviceListener(innerNodeListener);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530224 providerRegistry.unregister(this);
225 providerService = null;
mskala832d0472017-06-09 16:31:42 +0200226 retriedPortDiscoveryMap.clear();
Ray Milkey02710432018-02-13 17:08:28 -0800227 cfgService.unregisterConfigFactory(factory);
helenyrwufd296b62016-06-22 17:43:02 -0700228 scheduledTask.cancel(true);
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100229 connectionExecutor.shutdown();
230 pollingExecutor.shutdown();
Sanjay Seb5eebb2015-04-24 15:44:50 +0530231 log.info("Stopped");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530232 }
233
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700234
235 @Modified
236 public void modified(ComponentContext context) {
237 if (context != null) {
238 Dictionary<?, ?> properties = context.getProperties();
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700239 pollFrequency = Tools.getIntegerProperty(properties, POLL_FREQUENCY_SECONDS,
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100240 POLL_FREQUENCY_SECONDS_DEFAULT);
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700241 log.info("Configured. Poll frequency is configured to {} seconds", pollFrequency);
mskala832d0472017-06-09 16:31:42 +0200242
Thomas Vachuska4167c3f2018-10-16 07:16:31 -0700243 maxRetries = Tools.getIntegerProperty(properties, MAX_RETRIES, MAX_RETRIES_DEFAULT);
mskala832d0472017-06-09 16:31:42 +0200244 log.info("Configured. Number of retries is configured to {} times", maxRetries);
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700245 }
246 if (scheduledTask != null) {
247 scheduledTask.cancel(false);
248 }
249 scheduledTask = schedulePolling();
250 }
251
andreaeb70a942015-10-16 21:34:46 -0700252 public NetconfDeviceProvider() {
Andrea Campanella101417d2015-12-11 17:58:07 -0800253 super(new ProviderId(SCHEME_NAME, DEVICE_PROVIDER_PACKAGE));
Sanjay Se8dcfee2015-04-23 10:07:08 +0530254 }
255
256 @Override
257 public void triggerProbe(DeviceId deviceId) {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100258 //Not implemented, unused in netconf cases.
259 log.debug("Probing {} not implemented, not useful for NETCONF", deviceId);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530260 }
261
262 @Override
263 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100264 log.debug("Request role change {}, {}", deviceId, newRole);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700265 if (active) {
266 switch (newRole) {
267 case MASTER:
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100268 if (controller.getNetconfDevice(deviceId) == null) {
269 connectionExecutor.execute(exceptionSafe(() -> withDeviceLock(
270 () -> initiateConnection(deviceId), deviceId).run()));
271 log.debug("Accepting mastership role change to {} for device {}", newRole, deviceId);
272 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700273 break;
274 case STANDBY:
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100275 //TODO this issue a warning on the first election/connection
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700276 controller.disconnectDevice(deviceId, false);
gyewan.an91d7e7e2019-01-17 15:12:48 +0900277 withDeviceLock(
278 () -> initiateConnection(deviceId, newRole), deviceId).run();
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700279 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.STANDBY);
280 //else no-op
281 break;
282 case NONE:
283 controller.disconnectDevice(deviceId, false);
284 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
285 break;
286 default:
287 log.error("Unimplemented Mastership state : {}", newRole);
288
289 }
290 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530291 }
292
293 @Override
294 public boolean isReachable(DeviceId deviceId) {
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700295 boolean sessionExists =
296 Optional.ofNullable(controller.getDevicesMap().get(deviceId))
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100297 .map(NetconfDevice::isActive)
298 .orElse(false);
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700299 if (sessionExists) {
300 return true;
301 }
302
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700303 //FIXME this is a workaround util device state is shared
304 // between controller instances.
305 Device device = deviceService.getDevice(deviceId);
306 String ip;
307 int port;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700308 if (device != null) {
309 ip = device.annotations().value(IPADDRESS);
310 port = Integer.parseInt(device.annotations().value(PORT));
311 } else {
David K. Bainbridge56e90232018-12-18 23:25:08 -0800312 Triple<String, Integer, Optional<String>> info = extractIpPortPath(deviceId);
313 ip = info.getLeft();
314 port = info.getMiddle();
Sanjay Se8dcfee2015-04-23 10:07:08 +0530315 }
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700316 // FIXME just opening TCP session probably is not the appropriate
317 // method to test reachability.
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700318 //test connection to device opening a socket to it.
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700319 log.debug("Testing reachability for {}:{}", ip, port);
Andrea Campanella42e7b862018-09-21 11:56:48 +0200320 Socket socket = new Socket();
321 try {
322 socket.connect(new InetSocketAddress(ip, port), 1000);
Yuta HIGUCHI0454d702017-03-17 10:08:38 -0700323 log.debug("rechability of {}, {}, {}", deviceId, socket.isConnected(), !socket.isClosed());
Andrea Campanella42e7b862018-09-21 11:56:48 +0200324 boolean isConnected = socket.isConnected() && !socket.isClosed();
325 socket.close();
326 return isConnected;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700327 } catch (IOException e) {
328 log.info("Device {} is not reachable", deviceId);
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700329 log.debug(" error details", e);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700330 return false;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700331 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530332 }
333
Saurav Dasa2d37502016-03-25 17:50:40 -0700334 @Override
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100335 public void changePortState(DeviceId deviceId, PortNumber portNumber, boolean enable) {
Andrea Campanella32813682017-10-23 15:29:24 +0200336 Device device = deviceService.getDevice(deviceId);
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100337 if (device == null) {
338 log.error("Device {} is not present in the store", deviceId);
339 return;
Andrea Campanella32813682017-10-23 15:29:24 +0200340 }
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100341 if (!mastershipService.isLocalMaster(deviceId)) {
342 log.info("Not master but {}, not changing port state", mastershipService.getLocalRole(deviceId));
343 return;
344 }
345 if (!device.is(PortAdmin.class)) {
346 log.warn("Device {} does not support Port Admin", deviceId);
347 return;
348 }
349 PortAdmin portAdmin = device.as(PortAdmin.class);
350 CompletableFuture<Boolean> modified;
351 if (enable) {
352 modified = portAdmin.enable(portNumber);
353 } else {
354 modified = portAdmin.disable(portNumber);
355 }
356 modified.thenAccept(result -> {
357 if (result) {
358 Port port = deviceService.getPort(deviceId, portNumber);
359 //rebuilding port description with admin state changed.
360 providerService.portStatusChanged(deviceId,
361 DefaultPortDescription.builder()
362 .withPortNumber(portNumber)
363 .isEnabled(enable)
364 .isRemoved(false)
365 .type(port.type())
366 .portSpeed(port.portSpeed())
367 .annotations((SparseAnnotations) port.annotations())
368 .build());
369 } else {
370 log.warn("Your device {} port {} status can't be changed to {}",
371 deviceId, portNumber, enable);
372 }
373 });
Saurav Dasa2d37502016-03-25 17:50:40 -0700374 }
375
Thomas Vachuska5b38dc02018-05-10 15:24:40 -0700376 @Override
377 public void triggerDisconnect(DeviceId deviceId) {
378 log.debug("Forcing disconnect for device {}", deviceId);
379 controller.disconnectDevice(deviceId, true);
380 }
381
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100382 private ScheduledFuture schedulePolling() {
383 return pollingExecutor.scheduleAtFixedRate(exceptionSafe(this::checkAndUpdateDevices),
384 pollFrequency / 10,
385 pollFrequency, TimeUnit.SECONDS);
andreaeb70a942015-10-16 21:34:46 -0700386 }
387
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100388 private Runnable exceptionSafe(Runnable runnable) {
389 return () -> {
390 try {
391 runnable.run();
392 } catch (Exception e) {
393 log.error("Unhandled Exception", e);
394 }
395 };
396 }
397
398 //Connecting devices with initial config
andreaeb70a942015-10-16 21:34:46 -0700399 private void connectDevices() {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100400 Set<DeviceId> deviceSubjects = cfgService.getSubjects(DeviceId.class, NetconfDeviceConfig.class);
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200401 deviceSubjects.forEach(deviceId -> {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100402 connectionExecutor.execute(exceptionSafe(() -> runElectionFor(deviceId)));
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200403 });
helenyrwufd296b62016-06-22 17:43:02 -0700404 }
405
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100406 //updating keys and device info
407 private void checkAndUpdateDevices() {
408 Set<DeviceId> deviceSubjects = cfgService.getSubjects(DeviceId.class, NetconfDeviceConfig.class);
409 deviceSubjects.forEach(deviceId -> {
410 log.debug("check and update {}", deviceId);
411 NetconfDeviceConfig config = cfgService.getConfig(deviceId, NetconfDeviceConfig.class);
412 storeDeviceKey(config.sshKey(), config.username(), config.password(), deviceId);
413 discoverOrUpdatePorts(deviceId);
414 });
415 }
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700416
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100417 //Saving device keys in the store
418 private void storeDeviceKey(String sshKey, String username, String password, DeviceId deviceId) {
419 if (sshKey.equals("")) {
420 deviceKeyAdminService.addKey(
421 DeviceKey.createDeviceKeyUsingUsernamePassword(
422 DeviceKeyId.deviceKeyId(deviceId.toString()), null, username, password));
423 } else {
424 deviceKeyAdminService.addKey(
425 DeviceKey.createDeviceKeyUsingSshKey(
426 DeviceKeyId.deviceKeyId(deviceId.toString()), null, username, password, sshKey));
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700427 }
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100428 }
429
430 //running an election and applying the role to a given device
431 private void runElectionFor(DeviceId deviceId) {
432 //Triggering an election for the deviceId thus only master will connect
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700433 if (!deviceId.uri().getScheme().equals(SCHEME_NAME)) {
434 // not under my scheme, skipping
David K. Bainbridge1070de92019-01-29 22:23:11 -0800435 log.debug("{} not of schema {}, skipping", deviceId, SCHEME_NAME);
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700436 return;
437 }
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100438 connectionExecutor.submit(exceptionSafe(() -> {
439 CompletableFuture<MastershipRole> role = mastershipService.requestRoleFor(deviceId);
440 try {
441 roleChanged(deviceId, role.get());
442 } catch (InterruptedException | ExecutionException e) {
443 log.error("Can't get role for {} ", deviceId, e);
444 }
445 }));
446 }
447
448 //initiating the SSh connection the a given device.
449 private void initiateConnection(DeviceId deviceId) {
450
Andrea Campanella42e7b862018-09-21 11:56:48 +0200451 if (!isReachable(deviceId)) {
452 log.warn("Can't connect to device {}", deviceId);
453 return;
454 }
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100455
456 try {
457 NetconfDevice deviceNetconf = controller.connectDevice(deviceId);
458 if (deviceNetconf != null) {
459 //storeDeviceKey(config.sshKey(), config.username(), config.password(), deviceId);
460 NetconfDeviceConfig config = cfgService.getConfig(deviceId, NetconfDeviceConfig.class);
461 //getting the device description
462 DeviceDescription deviceDescription = getDeviceDescription(deviceId, config);
463 //connecting device to ONOS
464 log.debug("Connected NETCONF device {}, on {}:{} with username {}",
465 deviceId, config.ip(), config.port(),
466 (config.path().isPresent() ? "/" + config.path().get() : ""),
467 config.username());
468 providerService.deviceConnected(deviceId, deviceDescription);
469 } else {
470 mastershipService.relinquishMastership(deviceId);
471 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(deviceId.toString()));
472 log.error("Can't connect to NETCONF device {}", deviceId);
473 }
474 } catch (Exception e) {
475 mastershipService.relinquishMastership(deviceId);
476 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(deviceId.toString()));
477 throw new IllegalStateException(new NetconfException(
478 "Can't connect to NETCONF device " + deviceId, e));
479
480 }
481
482 }
483
484 private DeviceDescription getDeviceDescription(DeviceId deviceId, NetconfDeviceConfig config) {
485 Driver driver = driverService.getDriver(deviceId);
486 if (driver.hasBehaviour(DeviceDescriptionDiscovery.class)) {
487 final DriverData data = new DefaultDriverData(driver, deviceId);
488 final DriverHandler handler = new DefaultDriverHandler(data);
489 //creating the behaviour because the core has yet no notion of device.
490 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
491 driver.createBehaviour(handler, DeviceDescriptionDiscovery.class);
492 return getDeviceRepresentation(deviceId, config, deviceDescriptionDiscovery);
493 } else {
Andrea Campanella7eb01712019-02-06 10:02:15 +0100494 return existingOrEmptyDescription(deviceId, config);
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700495 }
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700496 }
497
Andrea Campanella7eb01712019-02-06 10:02:15 +0100498 private DeviceDescription getDeviceRepresentation(DeviceId deviceId, NetconfDeviceConfig config,
499 DeviceDescriptionDiscovery deviceDescriptionDiscovery) {
500
501 DeviceDescription existingOrEmptyDescription = existingOrEmptyDescription(deviceId, config);
502 DeviceDescription newDescription = deviceDescriptionDiscovery.discoverDeviceDetails();
503 if (newDescription == null) {
504 return existingOrEmptyDescription;
505 }
506 //merging and returning
507 return new DefaultDeviceDescription(newDescription, true,
508 DefaultAnnotations.merge((DefaultAnnotations) newDescription.annotations(),
509 existingOrEmptyDescription.annotations()));
510 }
511
512 private DeviceDescription existingOrEmptyDescription(DeviceId deviceId, NetconfDeviceConfig config) {
513 Device device = deviceService.getDevice(deviceId);
514
515 if (deviceService.getDevice(deviceId) != null) {
516 //getting the previous description
517 return new DefaultDeviceDescription(device.id().uri(), device.type(),
518 device.manufacturer(), device.hwVersion(),
519 device.swVersion(), device.serialNumber(),
520 device.chassisId(), (SparseAnnotations) device.annotations());
521 }
522
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100523 ChassisId cid = new ChassisId();
524 String ipAddress = config.ip().toString();
525 DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
526 .set(IPADDRESS, ipAddress)
527 .set(PORT, String.valueOf(config.port()))
528 .set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase());
529 if (config.path().isPresent()) {
530 annotations.set(PATH, config.path().get());
531 }
532 return new DefaultDeviceDescription(deviceId.uri(), Device.Type.SWITCH,
533 UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, cid, true, annotations.build());
534 }
535
gyewan.an91d7e7e2019-01-17 15:12:48 +0900536 /**
537 * Will appropriately create connections with the device.
538 * For Master role: will create secure transport and proxy sessions.
539 * For Standby role: will create only proxy session and disconnect secure transport session.
540 * For none role: will disconnect all sessions.
541 *
542 * @param deviceId device id
543 * @param newRole new role
544 */
545 private void initiateConnection(DeviceId deviceId, MastershipRole newRole) {
546 try {
547 if (isReachable(deviceId)) {
548 NetconfDevice device = null;
549 if (newRole.equals(MastershipRole.MASTER)) {
550 device = controller.connectDevice(deviceId, true);
551 } else if (newRole.equals(MastershipRole.STANDBY)) {
552 device = controller.connectDevice(deviceId, false);
553 }
554
555 if (device != null) {
556 providerService.receivedRoleReply(deviceId, newRole, newRole);
557 } else {
558 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
559 }
560
561 }
562 } catch (Exception e) {
563 if (deviceService.getDevice(deviceId) != null) {
564 providerService.deviceDisconnected(deviceId);
565 }
566 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(deviceId.toString()));
567 throw new IllegalStateException(new NetconfException(
568 "Can't connect to NETCONF device " + deviceId, e));
569 }
570 }
571
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100572
573 private void discoverOrUpdatePorts(DeviceId deviceId) {
574 retriedPortDiscoveryMap.put(deviceId, new AtomicInteger(0));
575 AtomicInteger count = retriedPortDiscoveryMap.get(deviceId);
576 //TODO this does not enable port discovery if port changes.
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530577 Device device = deviceService.getDevice(deviceId);
578 if (device == null) {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100579 log.debug("Cant' reach device {}, not updating ports", deviceId);
Andrea Campanella105736e2017-11-23 12:52:43 +0100580 return;
581 }
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100582 if (deviceService.getPorts(deviceId).isEmpty()
583 && count != null && count.getAndIncrement() < maxRetries) {
584 if (device.is(DeviceDescriptionDiscovery.class)) {
585 providerService.updatePorts(deviceId,
586 device.as(DeviceDescriptionDiscovery.class).discoverPortDetails());
587 } else {
588 log.warn("No DeviceDescirption behaviour for device {}", deviceId);
Andrea Campanella105736e2017-11-23 12:52:43 +0100589 }
Andrea Campanella105736e2017-11-23 12:52:43 +0100590
helenyrwufd296b62016-06-22 17:43:02 -0700591 }
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100592 updatePortStatistics(device);
helenyrwufd296b62016-06-22 17:43:02 -0700593 }
594
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530595 private void updatePortStatistics(Device device) {
596 if (device.is(PortStatisticsDiscovery.class)) {
597 PortStatisticsDiscovery d = device.as(PortStatisticsDiscovery.class);
Andrea Campanellac3627842017-04-04 18:06:54 +0200598 Collection<PortStatistics> portStatistics = d.discoverPortStatistics();
599 if (portStatistics != null) {
600 providerService.updatePortStatistics(device.id(),
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100601 portStatistics);
Andrea Campanellac3627842017-04-04 18:06:54 +0200602 }
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530603 } else {
Yuta HIGUCHIe4cb8cf2017-05-01 22:18:28 -0700604 log.debug("No port statistics getter behaviour for device {}",
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100605 device.id());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700606 }
607 }
608
Andrea Campanella42e7b862018-09-21 11:56:48 +0200609 private <U> U withDeviceLock(Supplier<U> task, DeviceId deviceId) {
610 final Lock lock = deviceLocks.get(deviceId);
611 lock.lock();
612 try {
613 return task.get();
614 } finally {
615 lock.unlock();
616 }
617 }
618
619 private Runnable withDeviceLock(Runnable task, DeviceId deviceId) {
620 // Wrapper of withDeviceLock(Supplier, ...) for void tasks.
621 return () -> withDeviceLock(() -> {
622 task.run();
623 return null;
624 }, deviceId);
625 }
626
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700627 /**
628 * Listener for configuration events.
629 */
andreaeb70a942015-10-16 21:34:46 -0700630 private class InternalNetworkConfigListener implements NetworkConfigListener {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530631
andreaeb70a942015-10-16 21:34:46 -0700632 @Override
633 public void event(NetworkConfigEvent event) {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100634 if (event.configClass().equals(NetconfDeviceConfig.class) && event.config().isPresent()) {
635 connectionExecutor.execute(exceptionSafe(() ->
636 runElectionFor((DeviceId) event.config().get().subject())));
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200637 } else {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100638 log.warn("Incorrect or absent Class for Netconf Configuration");
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200639 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530640 }
641
andreaeb70a942015-10-16 21:34:46 -0700642 @Override
643 public boolean isRelevant(NetworkConfigEvent event) {
Ray Milkey02710432018-02-13 17:08:28 -0800644 return (event.configClass().equals(NetconfDeviceConfig.class)) &&
andreaeb70a942015-10-16 21:34:46 -0700645 (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
646 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530647 }
648 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700649
650 /**
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100651 * Listener for Netconf Controller Events.
652 */
653 private class InnerNetconfDeviceListener implements NetconfDeviceListener {
654
655 @Override
656 public void deviceAdded(DeviceId deviceId) {
657 //no-op
658 log.debug("Netconf device {} added to Netconf controller", deviceId);
659 }
660
661 @Override
662 public void deviceRemoved(DeviceId deviceId) {
663 Preconditions.checkNotNull(deviceId, ISNULL);
664
665 if (deviceService.getDevice(deviceId) != null) {
666 providerService.deviceDisconnected(deviceId);
667 retriedPortDiscoveryMap.remove(deviceId);
668 log.debug("Netconf device {} removed from Netconf controller", deviceId);
669 } else {
670 log.warn("Netconf device {} does not exist in the store, " +
671 "it may already have been removed", deviceId);
672 }
673 }
674 }
675
676 /**
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700677 * Listener for core device events.
678 */
679 private class InternalDeviceListener implements DeviceListener {
680 @Override
681 public void event(DeviceEvent event) {
Andrea Campanellaa2a6c3c2018-12-11 12:56:38 +0100682 connectionExecutor.submit(exceptionSafe(() -> discoverOrUpdatePorts(event.subject().id())));
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700683 }
684
685 @Override
686 public boolean isRelevant(DeviceEvent event) {
Andrea Campanella42e7b862018-09-21 11:56:48 +0200687 if (event.type() != DeviceEvent.Type.DEVICE_ADDED &&
688 event.type() != DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED) {
Thomas Vachuska5b38dc02018-05-10 15:24:40 -0700689 return false;
690 }
Andrea Campanella968f93f2017-06-08 11:09:28 +0200691 return (SCHEME_NAME.equalsIgnoreCase(event.subject().annotations().value(AnnotationKeys.PROTOCOL)) ||
692 (SCHEME_NAME.equalsIgnoreCase(event.subject().id().uri().getScheme()))) &&
Michele Santuari576f09c2016-09-28 14:20:00 +0200693 mastershipService.isLocalMaster(event.subject().id());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700694 }
695 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530696}