blob: a0626dfb2628a998a8d9b72c61e7e6a7e7a57503 [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
Michele Santuari00cc1f72016-09-08 17:05:24 +020019import com.google.common.base.Objects;
andreaeb70a942015-10-16 21:34:46 -070020import com.google.common.base.Preconditions;
Andrea Campanella42e7b862018-09-21 11:56:48 +020021import 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.ApplicationId;
26import org.onosproject.core.CoreService;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070027import org.onosproject.mastership.MastershipService;
Marc De Leenheerb0d131c2016-03-01 20:34:59 -080028import org.onosproject.net.AnnotationKeys;
andreaeb70a942015-10-16 21:34:46 -070029import org.onosproject.net.DefaultAnnotations;
Sanjay Se8dcfee2015-04-23 10:07:08 +053030import org.onosproject.net.Device;
31import org.onosproject.net.DeviceId;
32import org.onosproject.net.MastershipRole;
Ray Milkey02710432018-02-13 17:08:28 -080033import org.onosproject.net.Port;
Saurav Dasa2d37502016-03-25 17:50:40 -070034import org.onosproject.net.PortNumber;
andreaeb70a942015-10-16 21:34:46 -070035import org.onosproject.net.SparseAnnotations;
Ray Milkey02710432018-02-13 17:08:28 -080036import org.onosproject.net.behaviour.PortAdmin;
andreaeb70a942015-10-16 21:34:46 -070037import org.onosproject.net.config.ConfigFactory;
38import org.onosproject.net.config.NetworkConfigEvent;
39import org.onosproject.net.config.NetworkConfigListener;
40import org.onosproject.net.config.NetworkConfigRegistry;
Andrea Campanella34cf65c2017-04-12 13:51:32 +020041import org.onosproject.net.config.basics.SubjectFactories;
Sanjay Se8dcfee2015-04-23 10:07:08 +053042import org.onosproject.net.device.DefaultDeviceDescription;
Andrea Campanella32813682017-10-23 15:29:24 +020043import org.onosproject.net.device.DefaultPortDescription;
Sanjay Se8dcfee2015-04-23 10:07:08 +053044import org.onosproject.net.device.DeviceDescription;
Andrea Campanella6c71a052016-04-22 11:56:31 -070045import org.onosproject.net.device.DeviceDescriptionDiscovery;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070046import org.onosproject.net.device.DeviceEvent;
47import org.onosproject.net.device.DeviceListener;
Sanjay Se8dcfee2015-04-23 10:07:08 +053048import org.onosproject.net.device.DeviceProvider;
49import org.onosproject.net.device.DeviceProviderRegistry;
50import org.onosproject.net.device.DeviceProviderService;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -080051import org.onosproject.net.device.DeviceService;
Andrea Campanellac3627842017-04-04 18:06:54 +020052import org.onosproject.net.device.PortStatistics;
Gaurav Agrawaldab4d772017-03-29 15:15:13 +053053import org.onosproject.net.device.PortStatisticsDiscovery;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070054import org.onosproject.net.key.DeviceKey;
55import org.onosproject.net.key.DeviceKeyAdminService;
56import org.onosproject.net.key.DeviceKeyId;
Sanjay Se8dcfee2015-04-23 10:07:08 +053057import org.onosproject.net.provider.AbstractProvider;
58import org.onosproject.net.provider.ProviderId;
andreaeb70a942015-10-16 21:34:46 -070059import org.onosproject.netconf.NetconfController;
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -070060import org.onosproject.netconf.NetconfDevice;
andreaeb70a942015-10-16 21:34:46 -070061import org.onosproject.netconf.NetconfDeviceListener;
Andrea Campanella8b1cb672016-01-25 13:58:58 -080062import org.onosproject.netconf.NetconfException;
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -070063import org.onosproject.netconf.config.NetconfDeviceConfig;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -070064import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070065import org.osgi.service.component.annotations.Activate;
66import org.osgi.service.component.annotations.Component;
67import org.osgi.service.component.annotations.Deactivate;
68import org.osgi.service.component.annotations.Modified;
69import org.osgi.service.component.annotations.Reference;
70import org.osgi.service.component.annotations.ReferenceCardinality;
Sanjay Se8dcfee2015-04-23 10:07:08 +053071import org.slf4j.Logger;
72
Andrea Campanella087ceb92015-12-07 09:58:34 -080073import java.io.IOException;
Andrea Campanella42e7b862018-09-21 11:56:48 +020074import java.net.InetSocketAddress;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070075import java.net.Socket;
76import java.net.URI;
77import java.net.URISyntaxException;
78import java.util.Arrays;
Andrea Campanellac3627842017-04-04 18:06:54 +020079import java.util.Collection;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -070080import java.util.Dictionary;
mskala832d0472017-06-09 16:31:42 +020081import java.util.Map;
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -070082import java.util.Optional;
Andrea Campanella34cf65c2017-04-12 13:51:32 +020083import java.util.Set;
Andrea Campanella32813682017-10-23 15:29:24 +020084import java.util.concurrent.CompletableFuture;
mskala832d0472017-06-09 16:31:42 +020085import java.util.concurrent.ConcurrentHashMap;
Andrea Campanella5c999e22016-03-01 15:12:53 -080086import java.util.concurrent.ExecutorService;
87import java.util.concurrent.Executors;
helenyrwufd296b62016-06-22 17:43:02 -070088import java.util.concurrent.ScheduledExecutorService;
89import java.util.concurrent.ScheduledFuture;
90import java.util.concurrent.TimeUnit;
mskala832d0472017-06-09 16:31:42 +020091import java.util.concurrent.atomic.AtomicInteger;
Andrea Campanella42e7b862018-09-21 11:56:48 +020092import java.util.concurrent.locks.Lock;
93import java.util.function.Supplier;
andreaeb70a942015-10-16 21:34:46 -070094
Yuta HIGUCHI1624df12016-07-21 16:54:33 -070095import static java.util.concurrent.Executors.newScheduledThreadPool;
Andrea Campanella5c999e22016-03-01 15:12:53 -080096import static org.onlab.util.Tools.groupedThreads;
andreaeb70a942015-10-16 21:34:46 -070097import static org.slf4j.LoggerFactory.getLogger;
98
Sanjay Se8dcfee2015-04-23 10:07:08 +053099/**
andreaeb70a942015-10-16 21:34:46 -0700100 * Provider which uses an NETCONF controller to detect device.
Sanjay Se8dcfee2015-04-23 10:07:08 +0530101 */
102@Component(immediate = true)
103public class NetconfDeviceProvider extends AbstractProvider
104 implements DeviceProvider {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700105
andreaeb70a942015-10-16 21:34:46 -0700106 private final Logger log = getLogger(getClass());
Sanjay Se8dcfee2015-04-23 10:07:08 +0530107
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700108 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Sanjay Se8dcfee2015-04-23 10:07:08 +0530109 protected DeviceProviderRegistry providerRegistry;
110
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700111 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella101417d2015-12-11 17:58:07 -0800112 protected NetconfController controller;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530113
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700114 @Reference(cardinality = ReferenceCardinality.MANDATORY)
andreaeb70a942015-10-16 21:34:46 -0700115 protected NetworkConfigRegistry cfgService;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530116
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700117 @Reference(cardinality = ReferenceCardinality.MANDATORY)
andreaeb70a942015-10-16 21:34:46 -0700118 protected CoreService coreService;
Thomas Vachuskad6811712015-04-29 21:37:04 -0700119
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700120 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700121 protected DeviceService deviceService;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -0800122
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700123 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700124 protected DeviceKeyAdminService deviceKeyAdminService;
125
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700126 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700127 protected MastershipService mastershipService;
128
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700129 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700130 protected ComponentConfigService componentConfigService;
131
132
Michele Santuari576f09c2016-09-28 14:20:00 +0200133 protected static final String APP_NAME = "org.onosproject.netconf";
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200134 protected static final String SCHEME_NAME = "netconf";
Andrea Campanella101417d2015-12-11 17:58:07 -0800135 private static final String DEVICE_PROVIDER_PACKAGE = "org.onosproject.netconf.provider.device";
136 private static final String UNKNOWN = "unknown";
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700137 protected static final String ISNULL = "NetconfDeviceInfo is null";
138 private static final String IPADDRESS = "ipaddress";
139 private static final String NETCONF = "netconf";
140 private static final String PORT = "port";
helenyrwufd296b62016-06-22 17:43:02 -0700141 private static final int CORE_POOL_SIZE = 10;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700142
Thomas Vachuskadb29dcf2017-03-31 11:26:19 -0700143 private static final int DEFAULT_POLL_FREQUENCY_SECONDS = 30;
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700144 //@Property(name = "pollFrequency", intValue = DEFAULT_POLL_FREQUENCY_SECONDS,
145 // label = "Configure poll frequency for port status and statistics; " +
146 // "default is 30 sec")
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700147 private int pollFrequency = DEFAULT_POLL_FREQUENCY_SECONDS;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530148
mskala832d0472017-06-09 16:31:42 +0200149 private static final int DEFAULT_MAX_RETRIES = 5;
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700150 //@Property(name = "maxRetries", intValue = DEFAULT_MAX_RETRIES,
151 // label = "Configure maximum allowed number of retries for obtaining list of ports; " +
152 // "default is 5 times")
mskala832d0472017-06-09 16:31:42 +0200153 private int maxRetries = DEFAULT_MAX_RETRIES;
154
Michal Mach26a90fa2017-06-07 11:12:46 +0200155 protected ExecutorService executor =
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700156 Executors.newFixedThreadPool(5, groupedThreads("onos/netconfdeviceprovider",
157 "device-installer-%d", log));
Yuta HIGUCHI1624df12016-07-21 16:54:33 -0700158 protected ScheduledExecutorService connectionExecutor
159 = newScheduledThreadPool(CORE_POOL_SIZE,
160 groupedThreads("onos/netconfdeviceprovider",
161 "connection-executor-%d", log));
Andrea Campanella5c999e22016-03-01 15:12:53 -0800162
Michele Santuari576f09c2016-09-28 14:20:00 +0200163 protected DeviceProviderService providerService;
andreaeb70a942015-10-16 21:34:46 -0700164 private NetconfDeviceListener innerNodeListener = new InnerNetconfDeviceListener();
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700165 private InternalDeviceListener deviceListener = new InternalDeviceListener();
mskala832d0472017-06-09 16:31:42 +0200166 private final Map<DeviceId, AtomicInteger> retriedPortDiscoveryMap = new ConcurrentHashMap<>();
Michele Santuari576f09c2016-09-28 14:20:00 +0200167 protected ScheduledFuture<?> scheduledTask;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530168
Ray Milkey02710432018-02-13 17:08:28 -0800169 protected final ConfigFactory factory =
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700170 // TODO consider moving Config registration to NETCONF ctl bundle
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200171 new ConfigFactory<DeviceId, NetconfDeviceConfig>(
172 SubjectFactories.DEVICE_SUBJECT_FACTORY,
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700173 NetconfDeviceConfig.class, NetconfDeviceConfig.CONFIG_KEY) {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200174 @Override
175 public NetconfDeviceConfig createConfig() {
176 return new NetconfDeviceConfig();
177 }
Ray Milkey02710432018-02-13 17:08:28 -0800178 };
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200179
Michele Santuari576f09c2016-09-28 14:20:00 +0200180 protected final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
andreaeb70a942015-10-16 21:34:46 -0700181 private ApplicationId appId;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700182 private boolean active;
Andrea Campanella42e7b862018-09-21 11:56:48 +0200183 private final Striped<Lock> deviceLocks = Striped.lock(30);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530184
Sanjay Se8dcfee2015-04-23 10:07:08 +0530185
186 @Activate
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700187 public void activate(ComponentContext context) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700188 active = true;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700189 componentConfigService.registerProperties(getClass());
Sanjay Se8dcfee2015-04-23 10:07:08 +0530190 providerService = providerRegistry.register(this);
Andrea Campanella101417d2015-12-11 17:58:07 -0800191 appId = coreService.registerApplication(APP_NAME);
Ray Milkey02710432018-02-13 17:08:28 -0800192 cfgService.registerConfigFactory(factory);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700193 cfgService.addListener(cfgListener);
andreaeb70a942015-10-16 21:34:46 -0700194 controller.addDeviceListener(innerNodeListener);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700195 deviceService.addListener(deviceListener);
Andrea Campanella7d8449b2016-03-02 10:16:42 -0800196 executor.execute(NetconfDeviceProvider.this::connectDevices);
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700197 modified(context);
Thomas Vachuskad6811712015-04-29 21:37:04 -0700198 log.info("Started");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530199 }
200
andreaeb70a942015-10-16 21:34:46 -0700201
Sanjay Se8dcfee2015-04-23 10:07:08 +0530202 @Deactivate
andreaeb70a942015-10-16 21:34:46 -0700203 public void deactivate() {
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700204 componentConfigService.unregisterProperties(getClass(), false);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700205 deviceService.removeListener(deviceListener);
206 active = false;
207 controller.getNetconfDevices().forEach(id -> {
208 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(id.toString()));
209 controller.disconnectDevice(id, true);
210 });
Andrea Campanella86294db2016-03-07 11:42:49 -0800211 controller.removeDeviceListener(innerNodeListener);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700212 deviceService.removeListener(deviceListener);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530213 providerRegistry.unregister(this);
214 providerService = null;
mskala832d0472017-06-09 16:31:42 +0200215 retriedPortDiscoveryMap.clear();
Ray Milkey02710432018-02-13 17:08:28 -0800216 cfgService.unregisterConfigFactory(factory);
helenyrwufd296b62016-06-22 17:43:02 -0700217 scheduledTask.cancel(true);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700218 executor.shutdown();
Sanjay Seb5eebb2015-04-24 15:44:50 +0530219 log.info("Stopped");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530220 }
221
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700222
223 @Modified
224 public void modified(ComponentContext context) {
225 if (context != null) {
226 Dictionary<?, ?> properties = context.getProperties();
227 pollFrequency = Tools.getIntegerProperty(properties, "pollFrequency",
228 DEFAULT_POLL_FREQUENCY_SECONDS);
229 log.info("Configured. Poll frequency is configured to {} seconds", pollFrequency);
mskala832d0472017-06-09 16:31:42 +0200230
231 maxRetries = Tools.getIntegerProperty(properties, "maxRetries",
232 DEFAULT_MAX_RETRIES);
233 log.info("Configured. Number of retries is configured to {} times", maxRetries);
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700234 }
235 if (scheduledTask != null) {
236 scheduledTask.cancel(false);
237 }
238 scheduledTask = schedulePolling();
239 }
240
andreaeb70a942015-10-16 21:34:46 -0700241 public NetconfDeviceProvider() {
Andrea Campanella101417d2015-12-11 17:58:07 -0800242 super(new ProviderId(SCHEME_NAME, DEVICE_PROVIDER_PACKAGE));
Sanjay Se8dcfee2015-04-23 10:07:08 +0530243 }
244
helenyrwufd296b62016-06-22 17:43:02 -0700245 // Checks connection to devices in the config file
246 // every DEFAULT_POLL_FREQUENCY_SECONDS seconds.
247 private ScheduledFuture schedulePolling() {
Yuta HIGUCHIc2b82e32017-03-10 14:33:41 -0800248 return connectionExecutor.scheduleAtFixedRate(exceptionSafe(this::checkAndUpdateDevices),
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700249 pollFrequency / 10,
250 pollFrequency, TimeUnit.SECONDS);
helenyrwufd296b62016-06-22 17:43:02 -0700251 }
252
Yuta HIGUCHIc2b82e32017-03-10 14:33:41 -0800253 private Runnable exceptionSafe(Runnable runnable) {
254 return new Runnable() {
255
256 @Override
257 public void run() {
258 try {
259 runnable.run();
260 } catch (Exception e) {
261 log.error("Unhandled Exception", e);
262 }
263 }
264 };
265 }
266
Sanjay Se8dcfee2015-04-23 10:07:08 +0530267 @Override
268 public void triggerProbe(DeviceId deviceId) {
andreaeb70a942015-10-16 21:34:46 -0700269 // TODO: This will be implemented later.
Yuta HIGUCHIe4cb8cf2017-05-01 22:18:28 -0700270 log.debug("Should be triggering probe on device {}", deviceId);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530271 }
272
273 @Override
274 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700275 if (active) {
276 switch (newRole) {
277 case MASTER:
Andrea Campanella42e7b862018-09-21 11:56:48 +0200278 withDeviceLock(
279 () -> initiateConnection(deviceId, newRole), deviceId).run();
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700280 log.debug("Accepting mastership role change to {} for device {}", newRole, deviceId);
281 break;
282 case STANDBY:
283 controller.disconnectDevice(deviceId, false);
284 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.STANDBY);
285 //else no-op
286 break;
287 case NONE:
288 controller.disconnectDevice(deviceId, false);
289 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
290 break;
291 default:
292 log.error("Unimplemented Mastership state : {}", newRole);
293
294 }
295 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530296 }
297
298 @Override
299 public boolean isReachable(DeviceId deviceId) {
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700300
301 boolean sessionExists =
302 Optional.ofNullable(controller.getDevicesMap().get(deviceId))
303 .map(NetconfDevice::isActive)
304 .orElse(false);
305 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 {
318 String[] info = deviceId.toString().split(":");
319 if (info.length == 3) {
320 ip = info[1];
321 port = Integer.parseInt(info[2]);
322 } else {
323 ip = Arrays.asList(info).stream().filter(el -> !el.equals(info[0])
324 && !el.equals(info[info.length - 1]))
325 .reduce((t, u) -> t + ":" + u)
326 .get();
327 log.debug("ip v6 {}", ip);
328 port = Integer.parseInt(info[info.length - 1]);
329 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530330 }
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700331 // FIXME just opening TCP session probably is not the appropriate
332 // method to test reachability.
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700333 //test connection to device opening a socket to it.
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700334 log.debug("Testing reachability for {}:{}", ip, port);
Andrea Campanella42e7b862018-09-21 11:56:48 +0200335 Socket socket = new Socket();
336 try {
337 socket.connect(new InetSocketAddress(ip, port), 1000);
Yuta HIGUCHI0454d702017-03-17 10:08:38 -0700338 log.debug("rechability of {}, {}, {}", deviceId, socket.isConnected(), !socket.isClosed());
Andrea Campanella42e7b862018-09-21 11:56:48 +0200339 boolean isConnected = socket.isConnected() && !socket.isClosed();
340 socket.close();
341 return isConnected;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700342 } catch (IOException e) {
343 log.info("Device {} is not reachable", deviceId);
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700344 log.debug(" error details", e);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700345 return false;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700346 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530347 }
348
Saurav Dasa2d37502016-03-25 17:50:40 -0700349 @Override
350 public void changePortState(DeviceId deviceId, PortNumber portNumber,
351 boolean enable) {
Andrea Campanella32813682017-10-23 15:29:24 +0200352 Device device = deviceService.getDevice(deviceId);
353 if (mastershipService.isLocalMaster(deviceId)) {
354 if (device.is(PortAdmin.class)) {
355 PortAdmin portAdmin =
356 device.as(PortAdmin.class);
357 CompletableFuture<Boolean> modified;
358 if (enable) {
359 modified = portAdmin.enable(portNumber);
360 } else {
361 modified = portAdmin.disable(portNumber);
362 }
363 modified.thenAccept(result -> {
364 if (result) {
365 Port port = deviceService.getPort(deviceId, portNumber);
366 //rebuilding port description with admin state changed.
367 providerService.portStatusChanged(deviceId,
Yuta HIGUCHI53e47962018-03-01 23:50:48 -0800368 DefaultPortDescription.builder()
369 .withPortNumber(portNumber)
370 .isEnabled(enable)
371 .isRemoved(false)
372 .type(port.type())
373 .portSpeed(port.portSpeed())
374 .annotations((SparseAnnotations) port.annotations())
375 .build());
Andrea Campanella32813682017-10-23 15:29:24 +0200376 } else {
377 log.warn("Your device {} port {} status can't be changed to {}",
378 deviceId, portNumber, enable);
379 }
380 });
381 } else {
382 log.warn("Device {} does not support Port Admin", deviceId);
383 }
384 } else {
385 log.debug("Not master but {}, not changing port state", mastershipService.getLocalRole(deviceId));
386 }
Saurav Dasa2d37502016-03-25 17:50:40 -0700387 }
388
Thomas Vachuska5b38dc02018-05-10 15:24:40 -0700389 @Override
390 public void triggerDisconnect(DeviceId deviceId) {
391 log.debug("Forcing disconnect for device {}", deviceId);
392 controller.disconnectDevice(deviceId, true);
393 }
394
andreaeb70a942015-10-16 21:34:46 -0700395 private class InnerNetconfDeviceListener implements NetconfDeviceListener {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530396
Andrea Campanella101417d2015-12-11 17:58:07 -0800397
andreaeb70a942015-10-16 21:34:46 -0700398 @Override
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700399 public void deviceAdded(DeviceId deviceId) {
400 //no-op
401 log.debug("Netconf device {} added to Netconf subController", deviceId);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530402 }
403
404 @Override
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700405 public void deviceRemoved(DeviceId deviceId) {
406 Preconditions.checkNotNull(deviceId, ISNULL);
helenyrwufd296b62016-06-22 17:43:02 -0700407
408 if (deviceService.getDevice(deviceId) != null) {
409 providerService.deviceDisconnected(deviceId);
mskala832d0472017-06-09 16:31:42 +0200410 retriedPortDiscoveryMap.remove(deviceId);
helenyrwufd296b62016-06-22 17:43:02 -0700411 log.debug("Netconf device {} removed from Netconf subController", deviceId);
412 } else {
413 log.warn("Netconf device {} does not exist in the store, " +
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530414 "it may already have been removed", deviceId);
helenyrwufd296b62016-06-22 17:43:02 -0700415 }
andreaeb70a942015-10-16 21:34:46 -0700416 }
417 }
418
andreaeb70a942015-10-16 21:34:46 -0700419 private void connectDevices() {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200420 Set<DeviceId> deviceSubjects =
421 cfgService.getSubjects(DeviceId.class, NetconfDeviceConfig.class);
422 deviceSubjects.forEach(deviceId -> {
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700423 connectDevice(cfgService.getConfig(deviceId, NetconfDeviceConfig.class));
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200424 });
helenyrwufd296b62016-06-22 17:43:02 -0700425 }
426
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700427
428 private void connectDevice(NetconfDeviceConfig config) {
429 if (config == null) {
430 return;
431 }
432 DeviceId deviceId = config.subject();
433 if (!deviceId.uri().getScheme().equals(SCHEME_NAME)) {
434 // not under my scheme, skipping
435 log.trace("{} not my scheme, skipping", deviceId);
436 return;
437 }
Andrea Campanella42e7b862018-09-21 11:56:48 +0200438 if (!isReachable(deviceId)) {
439 log.warn("Can't connect to device {}", deviceId);
440 return;
441 }
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700442 DeviceDescription deviceDescription = createDeviceRepresentation(deviceId, config);
443 log.debug("Connecting NETCONF device {}, on {}:{} with username {}",
444 deviceId, config.ip(), config.port(), config.username());
445 storeDeviceKey(config.sshKey(), config.username(), config.password(), deviceId);
Andrea Campanella105736e2017-11-23 12:52:43 +0100446 retriedPortDiscoveryMap.put(deviceId, new AtomicInteger(0));
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700447 if (deviceService.getDevice(deviceId) == null) {
448 providerService.deviceConnected(deviceId, deviceDescription);
449 }
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700450 }
451
Andrea Campanella105736e2017-11-23 12:52:43 +0100452 private void checkAndUpdateDevice(DeviceId deviceId, DeviceDescription deviceDescription, boolean newlyConnected) {
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530453 Device device = deviceService.getDevice(deviceId);
454 if (device == null) {
Andrea Campanella105736e2017-11-23 12:52:43 +0100455 log.debug("Device {} has not been added to store, since it's not reachable", deviceId);
456 return;
457 }
458 boolean isReachable = isReachable(deviceId);
459 if (!isReachable && deviceService.isAvailable(deviceId)) {
460 providerService.deviceDisconnected(deviceId);
461 return;
Andrea Campanella42e7b862018-09-21 11:56:48 +0200462 } else if (newlyConnected && mastershipService.isLocalMaster(deviceId)) {
Andrea Campanella105736e2017-11-23 12:52:43 +0100463 updateDeviceDescription(deviceId, deviceDescription, device);
464 }
465 if (isReachable && deviceService.isAvailable(deviceId) &&
466 mastershipService.isLocalMaster(deviceId)) {
467 //if ports are not discovered, retry the discovery
468 if (deviceService.getPorts(deviceId).isEmpty() &&
469 retriedPortDiscoveryMap.get(deviceId).getAndIncrement() < maxRetries) {
470 discoverPorts(deviceId);
471 }
472 updatePortStatistics(device);
473 }
474 }
475
476 private void updateDeviceDescription(DeviceId deviceId, DeviceDescription deviceDescription, Device device) {
477 if (device.is(DeviceDescriptionDiscovery.class)) {
478 if (mastershipService.isLocalMaster(deviceId)) {
479 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
480 device.as(DeviceDescriptionDiscovery.class);
481 DeviceDescription updatedDeviceDescription =
482 deviceDescriptionDiscovery.discoverDeviceDetails();
483 if (updatedDeviceDescription != null &&
484 !descriptionEquals(device, updatedDeviceDescription)) {
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530485 providerService.deviceConnected(
486 deviceId, new DefaultDeviceDescription(
Andrea Campanella105736e2017-11-23 12:52:43 +0100487 updatedDeviceDescription, true,
488 updatedDeviceDescription.annotations()));
Ray Milkey33306ba2018-09-20 13:27:25 -0700489 } else if (updatedDeviceDescription == null) {
Andrea Campanella105736e2017-11-23 12:52:43 +0100490 providerService.deviceConnected(
491 deviceId, new DefaultDeviceDescription(
492 deviceDescription, true,
493 deviceDescription.annotations()));
Konstantinos Kanonakis4d67dd82016-08-05 12:18:52 -0500494 }
helenyrwufd296b62016-06-22 17:43:02 -0700495 }
Andrea Campanella105736e2017-11-23 12:52:43 +0100496 } else {
497 log.warn("No DeviceDescriptionDiscovery behaviour for device {} " +
498 "using DefaultDeviceDescription", deviceId);
499 providerService.deviceConnected(
500 deviceId, new DefaultDeviceDescription(
501 deviceDescription, true, deviceDescription.annotations()));
helenyrwufd296b62016-06-22 17:43:02 -0700502 }
503 }
504
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530505 private void updatePortStatistics(Device device) {
506 if (device.is(PortStatisticsDiscovery.class)) {
507 PortStatisticsDiscovery d = device.as(PortStatisticsDiscovery.class);
Andrea Campanellac3627842017-04-04 18:06:54 +0200508 Collection<PortStatistics> portStatistics = d.discoverPortStatistics();
509 if (portStatistics != null) {
510 providerService.updatePortStatistics(device.id(),
511 portStatistics);
512 }
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530513 } else {
Yuta HIGUCHIe4cb8cf2017-05-01 22:18:28 -0700514 log.debug("No port statistics getter behaviour for device {}",
515 device.id());
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530516 }
517 }
518
Michele Santuari00cc1f72016-09-08 17:05:24 +0200519 private boolean descriptionEquals(Device device, DeviceDescription updatedDeviceDescription) {
Yuta HIGUCHIf381fc72017-01-03 10:39:36 -0800520 return Objects.equal(device.id().uri(), updatedDeviceDescription.deviceUri())
Michele Santuari00cc1f72016-09-08 17:05:24 +0200521 && Objects.equal(device.type(), updatedDeviceDescription.type())
522 && Objects.equal(device.manufacturer(), updatedDeviceDescription.manufacturer())
523 && Objects.equal(device.hwVersion(), updatedDeviceDescription.hwVersion())
524 && Objects.equal(device.swVersion(), updatedDeviceDescription.swVersion())
525 && Objects.equal(device.serialNumber(), updatedDeviceDescription.serialNumber())
526 && Objects.equal(device.chassisId(), updatedDeviceDescription.chassisId())
527 && Objects.equal(device.annotations(), updatedDeviceDescription.annotations());
528 }
529
helenyrwufd296b62016-06-22 17:43:02 -0700530 private void checkAndUpdateDevices() {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200531 Set<DeviceId> deviceSubjects =
532 cfgService.getSubjects(DeviceId.class, NetconfDeviceConfig.class);
533 deviceSubjects.forEach(deviceId -> {
534 NetconfDeviceConfig config =
535 cfgService.getConfig(deviceId, NetconfDeviceConfig.class);
536 DeviceDescription deviceDescription = createDeviceRepresentation(deviceId, config);
537 storeDeviceKey(config.sshKey(), config.username(), config.password(), deviceId);
Andrea Campanella105736e2017-11-23 12:52:43 +0100538 checkAndUpdateDevice(deviceId, deviceDescription, false);
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200539 });
andreaeb70a942015-10-16 21:34:46 -0700540 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530541
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200542 private DeviceDescription createDeviceRepresentation(DeviceId deviceId, NetconfDeviceConfig config) {
543 Preconditions.checkNotNull(deviceId, ISNULL);
544 //Netconf configuration object
545 ChassisId cid = new ChassisId();
546 String ipAddress = config.ip().toString();
547 SparseAnnotations annotations = DefaultAnnotations.builder()
548 .set(IPADDRESS, ipAddress)
549 .set(PORT, String.valueOf(config.port()))
550 .set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase())
Andrea Campanella42e7b862018-09-21 11:56:48 +0200551 .set(AnnotationKeys.PROVIDER_MARK_ONLINE, String.valueOf(true))
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200552 .build();
553 return new DefaultDeviceDescription(
554 deviceId.uri(),
555 Device.Type.SWITCH,
556 UNKNOWN, UNKNOWN,
557 UNKNOWN, UNKNOWN,
558 cid, false,
559 annotations);
560 }
561
562 private void storeDeviceKey(String sshKey, String username, String password, DeviceId deviceId) {
563 if (sshKey.equals("")) {
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600564 deviceKeyAdminService.addKey(
565 DeviceKey.createDeviceKeyUsingUsernamePassword(
566 DeviceKeyId.deviceKeyId(deviceId.toString()),
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200567 null, username, password));
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600568 } else {
569 deviceKeyAdminService.addKey(
570 DeviceKey.createDeviceKeyUsingSshKey(
571 DeviceKeyId.deviceKeyId(deviceId.toString()),
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200572 null, username, password,
573 sshKey));
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600574 }
575 }
576
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700577 private void initiateConnection(DeviceId deviceId, MastershipRole newRole) {
578 try {
579 if (isReachable(deviceId)) {
Andrea Campanella42e7b862018-09-21 11:56:48 +0200580 NetconfDevice device = controller.connectDevice(deviceId);
581 if (device != null) {
582 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.MASTER);
583 } else {
584 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
585 }
586
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700587 }
588 } catch (Exception e) {
589 if (deviceService.getDevice(deviceId) != null) {
590 providerService.deviceDisconnected(deviceId);
591 }
592 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(deviceId.toString()));
Ray Milkey986a47a2018-01-25 11:38:51 -0800593 throw new IllegalStateException(new NetconfException(
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200594 "Can't connect to NETCONF device " + deviceId, e));
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700595
596 }
597 }
598
599 private void discoverPorts(DeviceId deviceId) {
600 Device device = deviceService.getDevice(deviceId);
Andrea Campanella6c71a052016-04-22 11:56:31 -0700601 //TODO remove when PortDiscovery is removed from master
Ray Milkey640fedd2018-02-08 09:02:26 -0800602 if (device.is(DeviceDescriptionDiscovery.class)) {
Andrea Campanella6c71a052016-04-22 11:56:31 -0700603 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
604 device.as(DeviceDescriptionDiscovery.class);
605 providerService.updatePorts(deviceId,
606 deviceDescriptionDiscovery.discoverPortDetails());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700607 } else {
608 log.warn("No portGetter behaviour for device {}", deviceId);
609 }
Gaurav Agrawaldab4d772017-03-29 15:15:13 +0530610
611 // Port statistics discovery
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530612 updatePortStatistics(device);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700613 }
614
615 /**
616 * Return the DeviceId about the device containing the URI.
617 *
Andrea Campanella6c71a052016-04-22 11:56:31 -0700618 * @param ip IP address
Ray Milkeyd4334db2016-04-05 17:39:44 -0700619 * @param port port number
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700620 * @return DeviceId
621 */
622 public DeviceId getDeviceId(String ip, int port) {
623 try {
624 return DeviceId.deviceId(new URI(NETCONF, ip + ":" + port, null));
625 } catch (URISyntaxException e) {
626 throw new IllegalArgumentException("Unable to build deviceID for device "
627 + ip + ":" + port, e);
628 }
629 }
630
Andrea Campanella42e7b862018-09-21 11:56:48 +0200631 private <U> U withDeviceLock(Supplier<U> task, DeviceId deviceId) {
632 final Lock lock = deviceLocks.get(deviceId);
633 lock.lock();
634 try {
635 return task.get();
636 } finally {
637 lock.unlock();
638 }
639 }
640
641 private Runnable withDeviceLock(Runnable task, DeviceId deviceId) {
642 // Wrapper of withDeviceLock(Supplier, ...) for void tasks.
643 return () -> withDeviceLock(() -> {
644 task.run();
645 return null;
646 }, deviceId);
647 }
648
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700649 /**
650 * Listener for configuration events.
651 */
andreaeb70a942015-10-16 21:34:46 -0700652 private class InternalNetworkConfigListener implements NetworkConfigListener {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530653
andreaeb70a942015-10-16 21:34:46 -0700654
655 @Override
656 public void event(NetworkConfigEvent event) {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200657 if (event.configClass().equals(NetconfDeviceConfig.class)) {
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700658 executor.execute(() -> connectDevice((NetconfDeviceConfig) event.config().get()));
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200659 } else {
660 log.warn("Injecting device via this Json is deprecated, " +
661 "please put configuration under devices/ as shown in the wiki");
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200662 }
663
Sanjay Se8dcfee2015-04-23 10:07:08 +0530664 }
665
andreaeb70a942015-10-16 21:34:46 -0700666 @Override
667 public boolean isRelevant(NetworkConfigEvent event) {
Ray Milkey02710432018-02-13 17:08:28 -0800668 return (event.configClass().equals(NetconfDeviceConfig.class)) &&
andreaeb70a942015-10-16 21:34:46 -0700669 (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
670 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530671 }
672 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700673
674 /**
675 * Listener for core device events.
676 */
677 private class InternalDeviceListener implements DeviceListener {
678 @Override
679 public void event(DeviceEvent event) {
Andrea Campanella42e7b862018-09-21 11:56:48 +0200680 DeviceId deviceId = event.subject().id();
681 if (event.type() == DeviceEvent.Type.DEVICE_ADDED && !deviceService.isAvailable(event.subject().id())) {
682 try {
683 checkAndUpdateDevice(deviceId, null, true);
684 } catch (Exception e) {
685 log.error("Unhandled exception checking {}", deviceId, e);
686 }
687 }
688 if (deviceService.isAvailable(event.subject().id())) {
689 executor.execute(() -> discoverPorts(event.subject().id()));
690 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700691 }
692
693 @Override
694 public boolean isRelevant(DeviceEvent event) {
Andrea Campanella42e7b862018-09-21 11:56:48 +0200695 if (event.type() != DeviceEvent.Type.DEVICE_ADDED &&
696 event.type() != DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED) {
Thomas Vachuska5b38dc02018-05-10 15:24:40 -0700697 return false;
698 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700699 if (mastershipService.getMasterFor(event.subject().id()) == null) {
700 return true;
701 }
Andrea Campanella968f93f2017-06-08 11:09:28 +0200702 return (SCHEME_NAME.equalsIgnoreCase(event.subject().annotations().value(AnnotationKeys.PROTOCOL)) ||
703 (SCHEME_NAME.equalsIgnoreCase(event.subject().id().uri().getScheme()))) &&
Michele Santuari576f09c2016-09-28 14:20:00 +0200704 mastershipService.isLocalMaster(event.subject().id());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700705 }
706 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530707}