blob: 24e1d89efb255c1811c09f9afa2dad51e9d5dafb [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
Andrea Campanella34cf65c2017-04-12 13:51:32 +020019import com.fasterxml.jackson.databind.ObjectMapper;
20import com.fasterxml.jackson.databind.node.ObjectNode;
Michele Santuari00cc1f72016-09-08 17:05:24 +020021import com.google.common.base.Objects;
andreaeb70a942015-10-16 21:34:46 -070022import com.google.common.base.Preconditions;
Andrea Campanella34cf65c2017-04-12 13:51:32 +020023import com.google.common.collect.ImmutableList;
Sanjay Se8dcfee2015-04-23 10:07:08 +053024import org.apache.felix.scr.annotations.Activate;
25import org.apache.felix.scr.annotations.Component;
26import org.apache.felix.scr.annotations.Deactivate;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -070027import org.apache.felix.scr.annotations.Modified;
28import org.apache.felix.scr.annotations.Property;
Sanjay Se8dcfee2015-04-23 10:07:08 +053029import org.apache.felix.scr.annotations.Reference;
30import org.apache.felix.scr.annotations.ReferenceCardinality;
31import org.onlab.packet.ChassisId;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -070032import org.onlab.util.Tools;
33import org.onosproject.cfg.ComponentConfigService;
andreaeb70a942015-10-16 21:34:46 -070034import org.onosproject.core.ApplicationId;
35import org.onosproject.core.CoreService;
Andrea Campanella32813682017-10-23 15:29:24 +020036import org.onosproject.net.Port;
37import org.onosproject.net.behaviour.PortAdmin;
Ray Milkey6c013742017-08-15 10:16:43 -070038import org.onosproject.net.config.ConfigException;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070039import org.onosproject.mastership.MastershipService;
Marc De Leenheerb0d131c2016-03-01 20:34:59 -080040import org.onosproject.net.AnnotationKeys;
andreaeb70a942015-10-16 21:34:46 -070041import org.onosproject.net.DefaultAnnotations;
Sanjay Se8dcfee2015-04-23 10:07:08 +053042import org.onosproject.net.Device;
43import org.onosproject.net.DeviceId;
44import org.onosproject.net.MastershipRole;
Saurav Dasa2d37502016-03-25 17:50:40 -070045import org.onosproject.net.PortNumber;
andreaeb70a942015-10-16 21:34:46 -070046import org.onosproject.net.SparseAnnotations;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -080047import org.onosproject.net.behaviour.PortDiscovery;
andreaeb70a942015-10-16 21:34:46 -070048import org.onosproject.net.config.ConfigFactory;
49import org.onosproject.net.config.NetworkConfigEvent;
50import org.onosproject.net.config.NetworkConfigListener;
51import org.onosproject.net.config.NetworkConfigRegistry;
Andrea Campanella34cf65c2017-04-12 13:51:32 +020052import org.onosproject.net.config.basics.SubjectFactories;
Sanjay Se8dcfee2015-04-23 10:07:08 +053053import org.onosproject.net.device.DefaultDeviceDescription;
Andrea Campanella32813682017-10-23 15:29:24 +020054import org.onosproject.net.device.DefaultPortDescription;
Sanjay Se8dcfee2015-04-23 10:07:08 +053055import org.onosproject.net.device.DeviceDescription;
Andrea Campanella6c71a052016-04-22 11:56:31 -070056import org.onosproject.net.device.DeviceDescriptionDiscovery;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070057import org.onosproject.net.device.DeviceEvent;
58import org.onosproject.net.device.DeviceListener;
Sanjay Se8dcfee2015-04-23 10:07:08 +053059import org.onosproject.net.device.DeviceProvider;
60import org.onosproject.net.device.DeviceProviderRegistry;
61import org.onosproject.net.device.DeviceProviderService;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -080062import org.onosproject.net.device.DeviceService;
Andrea Campanellac3627842017-04-04 18:06:54 +020063import org.onosproject.net.device.PortStatistics;
Gaurav Agrawaldab4d772017-03-29 15:15:13 +053064import org.onosproject.net.device.PortStatisticsDiscovery;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070065import org.onosproject.net.key.DeviceKey;
66import org.onosproject.net.key.DeviceKeyAdminService;
67import org.onosproject.net.key.DeviceKeyId;
Sanjay Se8dcfee2015-04-23 10:07:08 +053068import org.onosproject.net.provider.AbstractProvider;
69import org.onosproject.net.provider.ProviderId;
andreaeb70a942015-10-16 21:34:46 -070070import org.onosproject.netconf.NetconfController;
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -070071import org.onosproject.netconf.NetconfDevice;
andreaeb70a942015-10-16 21:34:46 -070072import org.onosproject.netconf.NetconfDeviceListener;
Andrea Campanella8b1cb672016-01-25 13:58:58 -080073import org.onosproject.netconf.NetconfException;
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -070074import org.onosproject.netconf.config.NetconfDeviceConfig;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -070075import org.osgi.service.component.ComponentContext;
Sanjay Se8dcfee2015-04-23 10:07:08 +053076import org.slf4j.Logger;
77
Andrea Campanella087ceb92015-12-07 09:58:34 -080078import java.io.IOException;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070079import java.net.Socket;
80import java.net.URI;
81import java.net.URISyntaxException;
82import java.util.Arrays;
Andrea Campanellac3627842017-04-04 18:06:54 +020083import java.util.Collection;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -070084import java.util.Dictionary;
Andrea Campanella34cf65c2017-04-12 13:51:32 +020085import java.util.List;
mskala832d0472017-06-09 16:31:42 +020086import java.util.Map;
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -070087import java.util.Optional;
Andrea Campanella34cf65c2017-04-12 13:51:32 +020088import java.util.Set;
Andrea Campanella32813682017-10-23 15:29:24 +020089import java.util.concurrent.CompletableFuture;
mskala832d0472017-06-09 16:31:42 +020090import java.util.concurrent.ConcurrentHashMap;
Andrea Campanella5c999e22016-03-01 15:12:53 -080091import java.util.concurrent.ExecutorService;
92import java.util.concurrent.Executors;
helenyrwufd296b62016-06-22 17:43:02 -070093import java.util.concurrent.ScheduledExecutorService;
94import java.util.concurrent.ScheduledFuture;
95import java.util.concurrent.TimeUnit;
mskala832d0472017-06-09 16:31:42 +020096import java.util.concurrent.atomic.AtomicInteger;
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;
andreaeb70a942015-10-16 21:34:46 -0700100import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
101import static org.slf4j.LoggerFactory.getLogger;
102
Sanjay Se8dcfee2015-04-23 10:07:08 +0530103/**
andreaeb70a942015-10-16 21:34:46 -0700104 * Provider which uses an NETCONF controller to detect device.
Sanjay Se8dcfee2015-04-23 10:07:08 +0530105 */
106@Component(immediate = true)
107public class NetconfDeviceProvider extends AbstractProvider
108 implements DeviceProvider {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700109
andreaeb70a942015-10-16 21:34:46 -0700110 private final Logger log = getLogger(getClass());
Sanjay Se8dcfee2015-04-23 10:07:08 +0530111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
113 protected DeviceProviderRegistry providerRegistry;
114
andreaeb70a942015-10-16 21:34:46 -0700115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella101417d2015-12-11 17:58:07 -0800116 protected NetconfController controller;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
andreaeb70a942015-10-16 21:34:46 -0700119 protected NetworkConfigRegistry cfgService;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530120
Thomas Vachuskad6811712015-04-29 21:37:04 -0700121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
andreaeb70a942015-10-16 21:34:46 -0700122 protected CoreService coreService;
Thomas Vachuskad6811712015-04-29 21:37:04 -0700123
Aaron Kruglikov17b4c852016-01-15 16:37:04 -0800124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700125 protected DeviceService deviceService;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -0800126
127 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700128 protected DeviceKeyAdminService deviceKeyAdminService;
129
130 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
131 protected MastershipService mastershipService;
132
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700133 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
134 protected ComponentConfigService componentConfigService;
135
136
Michele Santuari576f09c2016-09-28 14:20:00 +0200137 protected static final String APP_NAME = "org.onosproject.netconf";
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200138 protected static final String SCHEME_NAME = "netconf";
Andrea Campanella101417d2015-12-11 17:58:07 -0800139 private static final String DEVICE_PROVIDER_PACKAGE = "org.onosproject.netconf.provider.device";
140 private static final String UNKNOWN = "unknown";
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700141 protected static final String ISNULL = "NetconfDeviceInfo is null";
142 private static final String IPADDRESS = "ipaddress";
143 private static final String NETCONF = "netconf";
144 private static final String PORT = "port";
helenyrwufd296b62016-06-22 17:43:02 -0700145 private static final int CORE_POOL_SIZE = 10;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700146
Thomas Vachuskadb29dcf2017-03-31 11:26:19 -0700147 private static final int DEFAULT_POLL_FREQUENCY_SECONDS = 30;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700148 @Property(name = "pollFrequency", intValue = DEFAULT_POLL_FREQUENCY_SECONDS,
149 label = "Configure poll frequency for port status and statistics; " +
Thomas Vachuskadb29dcf2017-03-31 11:26:19 -0700150 "default is 30 sec")
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700151 private int pollFrequency = DEFAULT_POLL_FREQUENCY_SECONDS;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530152
mskala832d0472017-06-09 16:31:42 +0200153 private static final int DEFAULT_MAX_RETRIES = 5;
154 @Property(name = "maxRetries", intValue = DEFAULT_MAX_RETRIES,
155 label = "Configure maximum allowed number of retries for obtaining list of ports; " +
156 "default is 5 times")
157 private int maxRetries = DEFAULT_MAX_RETRIES;
158
Michal Mach26a90fa2017-06-07 11:12:46 +0200159 protected ExecutorService executor =
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700160 Executors.newFixedThreadPool(5, groupedThreads("onos/netconfdeviceprovider",
161 "device-installer-%d", log));
Yuta HIGUCHI1624df12016-07-21 16:54:33 -0700162 protected ScheduledExecutorService connectionExecutor
163 = newScheduledThreadPool(CORE_POOL_SIZE,
164 groupedThreads("onos/netconfdeviceprovider",
165 "connection-executor-%d", log));
Andrea Campanella5c999e22016-03-01 15:12:53 -0800166
Michele Santuari576f09c2016-09-28 14:20:00 +0200167 protected DeviceProviderService providerService;
andreaeb70a942015-10-16 21:34:46 -0700168 private NetconfDeviceListener innerNodeListener = new InnerNetconfDeviceListener();
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700169 private InternalDeviceListener deviceListener = new InternalDeviceListener();
mskala832d0472017-06-09 16:31:42 +0200170 private final Map<DeviceId, AtomicInteger> retriedPortDiscoveryMap = new ConcurrentHashMap<>();
Michele Santuari576f09c2016-09-28 14:20:00 +0200171 protected ScheduledFuture<?> scheduledTask;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530172
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200173 protected final List<ConfigFactory> factories = ImmutableList.of(
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700174 // TODO consider moving Config registration to NETCONF ctl bundle
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200175 new ConfigFactory<DeviceId, NetconfDeviceConfig>(
176 SubjectFactories.DEVICE_SUBJECT_FACTORY,
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700177 NetconfDeviceConfig.class, NetconfDeviceConfig.CONFIG_KEY) {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200178 @Override
179 public NetconfDeviceConfig createConfig() {
180 return new NetconfDeviceConfig();
181 }
182 },
andreaeb70a942015-10-16 21:34:46 -0700183 new ConfigFactory<ApplicationId, NetconfProviderConfig>(APP_SUBJECT_FACTORY,
184 NetconfProviderConfig.class,
Palash Kalac3ffad92017-06-09 21:18:19 +0900185 "netconf_devices",
andreaeb70a942015-10-16 21:34:46 -0700186 true) {
187 @Override
188 public NetconfProviderConfig createConfig() {
189 return new NetconfProviderConfig();
190 }
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200191 });
192
Michele Santuari576f09c2016-09-28 14:20:00 +0200193 protected final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
andreaeb70a942015-10-16 21:34:46 -0700194 private ApplicationId appId;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700195 private boolean active;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530196
Sanjay Se8dcfee2015-04-23 10:07:08 +0530197
198 @Activate
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700199 public void activate(ComponentContext context) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700200 active = true;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700201 componentConfigService.registerProperties(getClass());
Sanjay Se8dcfee2015-04-23 10:07:08 +0530202 providerService = providerRegistry.register(this);
Andrea Campanella101417d2015-12-11 17:58:07 -0800203 appId = coreService.registerApplication(APP_NAME);
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200204 factories.forEach(cfgService::registerConfigFactory);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700205 cfgService.addListener(cfgListener);
andreaeb70a942015-10-16 21:34:46 -0700206 controller.addDeviceListener(innerNodeListener);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700207 deviceService.addListener(deviceListener);
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200208 translateConfig();
Andrea Campanella7d8449b2016-03-02 10:16:42 -0800209 executor.execute(NetconfDeviceProvider.this::connectDevices);
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700210 modified(context);
Thomas Vachuskad6811712015-04-29 21:37:04 -0700211 log.info("Started");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530212 }
213
andreaeb70a942015-10-16 21:34:46 -0700214
Sanjay Se8dcfee2015-04-23 10:07:08 +0530215 @Deactivate
andreaeb70a942015-10-16 21:34:46 -0700216 public void deactivate() {
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);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700225 deviceService.removeListener(deviceListener);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530226 providerRegistry.unregister(this);
227 providerService = null;
mskala832d0472017-06-09 16:31:42 +0200228 retriedPortDiscoveryMap.clear();
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200229 factories.forEach(cfgService::unregisterConfigFactory);
helenyrwufd296b62016-06-22 17:43:02 -0700230 scheduledTask.cancel(true);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700231 executor.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();
240 pollFrequency = Tools.getIntegerProperty(properties, "pollFrequency",
241 DEFAULT_POLL_FREQUENCY_SECONDS);
242 log.info("Configured. Poll frequency is configured to {} seconds", pollFrequency);
mskala832d0472017-06-09 16:31:42 +0200243
244 maxRetries = Tools.getIntegerProperty(properties, "maxRetries",
245 DEFAULT_MAX_RETRIES);
246 log.info("Configured. Number of retries is configured to {} times", maxRetries);
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700247 }
248 if (scheduledTask != null) {
249 scheduledTask.cancel(false);
250 }
251 scheduledTask = schedulePolling();
252 }
253
andreaeb70a942015-10-16 21:34:46 -0700254 public NetconfDeviceProvider() {
Andrea Campanella101417d2015-12-11 17:58:07 -0800255 super(new ProviderId(SCHEME_NAME, DEVICE_PROVIDER_PACKAGE));
Sanjay Se8dcfee2015-04-23 10:07:08 +0530256 }
257
helenyrwufd296b62016-06-22 17:43:02 -0700258 // Checks connection to devices in the config file
259 // every DEFAULT_POLL_FREQUENCY_SECONDS seconds.
260 private ScheduledFuture schedulePolling() {
Yuta HIGUCHIc2b82e32017-03-10 14:33:41 -0800261 return connectionExecutor.scheduleAtFixedRate(exceptionSafe(this::checkAndUpdateDevices),
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700262 pollFrequency / 10,
263 pollFrequency, TimeUnit.SECONDS);
helenyrwufd296b62016-06-22 17:43:02 -0700264 }
265
Yuta HIGUCHIc2b82e32017-03-10 14:33:41 -0800266 private Runnable exceptionSafe(Runnable runnable) {
267 return new Runnable() {
268
269 @Override
270 public void run() {
271 try {
272 runnable.run();
273 } catch (Exception e) {
274 log.error("Unhandled Exception", e);
275 }
276 }
277 };
278 }
279
Sanjay Se8dcfee2015-04-23 10:07:08 +0530280 @Override
281 public void triggerProbe(DeviceId deviceId) {
andreaeb70a942015-10-16 21:34:46 -0700282 // TODO: This will be implemented later.
Yuta HIGUCHIe4cb8cf2017-05-01 22:18:28 -0700283 log.debug("Should be triggering probe on device {}", deviceId);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530284 }
285
286 @Override
287 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700288 if (active) {
289 switch (newRole) {
290 case MASTER:
291 initiateConnection(deviceId, newRole);
292 log.debug("Accepting mastership role change to {} for device {}", newRole, deviceId);
293 break;
294 case STANDBY:
295 controller.disconnectDevice(deviceId, false);
296 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.STANDBY);
297 //else no-op
298 break;
299 case NONE:
300 controller.disconnectDevice(deviceId, false);
301 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
302 break;
303 default:
304 log.error("Unimplemented Mastership state : {}", newRole);
305
306 }
307 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530308 }
309
310 @Override
311 public boolean isReachable(DeviceId deviceId) {
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700312
313 boolean sessionExists =
314 Optional.ofNullable(controller.getDevicesMap().get(deviceId))
315 .map(NetconfDevice::isActive)
316 .orElse(false);
317 if (sessionExists) {
318 return true;
319 }
320
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700321 //FIXME this is a workaround util device state is shared
322 // between controller instances.
323 Device device = deviceService.getDevice(deviceId);
324 String ip;
325 int port;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700326 if (device != null) {
327 ip = device.annotations().value(IPADDRESS);
328 port = Integer.parseInt(device.annotations().value(PORT));
329 } else {
330 String[] info = deviceId.toString().split(":");
331 if (info.length == 3) {
332 ip = info[1];
333 port = Integer.parseInt(info[2]);
334 } else {
335 ip = Arrays.asList(info).stream().filter(el -> !el.equals(info[0])
336 && !el.equals(info[info.length - 1]))
337 .reduce((t, u) -> t + ":" + u)
338 .get();
339 log.debug("ip v6 {}", ip);
340 port = Integer.parseInt(info[info.length - 1]);
341 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530342 }
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700343 // FIXME just opening TCP session probably is not the appropriate
344 // method to test reachability.
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700345 //test connection to device opening a socket to it.
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700346 log.debug("Testing reachability for {}:{}", ip, port);
Yuta HIGUCHI70c21472017-04-20 20:40:46 -0700347 try (Socket socket = new Socket(ip, port)) {
Yuta HIGUCHI0454d702017-03-17 10:08:38 -0700348 log.debug("rechability of {}, {}, {}", deviceId, socket.isConnected(), !socket.isClosed());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700349 return socket.isConnected() && !socket.isClosed();
350 } catch (IOException e) {
351 log.info("Device {} is not reachable", deviceId);
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700352 log.debug(" error details", e);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700353 return false;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700354 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530355 }
356
Saurav Dasa2d37502016-03-25 17:50:40 -0700357 @Override
358 public void changePortState(DeviceId deviceId, PortNumber portNumber,
359 boolean enable) {
Andrea Campanella32813682017-10-23 15:29:24 +0200360 Device device = deviceService.getDevice(deviceId);
361 if (mastershipService.isLocalMaster(deviceId)) {
362 if (device.is(PortAdmin.class)) {
363 PortAdmin portAdmin =
364 device.as(PortAdmin.class);
365 CompletableFuture<Boolean> modified;
366 if (enable) {
367 modified = portAdmin.enable(portNumber);
368 } else {
369 modified = portAdmin.disable(portNumber);
370 }
371 modified.thenAccept(result -> {
372 if (result) {
373 Port port = deviceService.getPort(deviceId, portNumber);
374 //rebuilding port description with admin state changed.
375 providerService.portStatusChanged(deviceId,
376 new DefaultPortDescription(portNumber, enable, false,
377 port.type(), port.portSpeed(),
378 (SparseAnnotations) port.annotations()));
379 } else {
380 log.warn("Your device {} port {} status can't be changed to {}",
381 deviceId, portNumber, enable);
382 }
383 });
384 } else {
385 log.warn("Device {} does not support Port Admin", deviceId);
386 }
387 } else {
388 log.debug("Not master but {}, not changing port state", mastershipService.getLocalRole(deviceId));
389 }
Saurav Dasa2d37502016-03-25 17:50:40 -0700390 }
391
andreaeb70a942015-10-16 21:34:46 -0700392 private class InnerNetconfDeviceListener implements NetconfDeviceListener {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530393
Andrea Campanella101417d2015-12-11 17:58:07 -0800394
andreaeb70a942015-10-16 21:34:46 -0700395 @Override
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700396 public void deviceAdded(DeviceId deviceId) {
397 //no-op
398 log.debug("Netconf device {} added to Netconf subController", deviceId);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530399 }
400
401 @Override
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700402 public void deviceRemoved(DeviceId deviceId) {
403 Preconditions.checkNotNull(deviceId, ISNULL);
helenyrwufd296b62016-06-22 17:43:02 -0700404
405 if (deviceService.getDevice(deviceId) != null) {
406 providerService.deviceDisconnected(deviceId);
mskala832d0472017-06-09 16:31:42 +0200407 retriedPortDiscoveryMap.remove(deviceId);
helenyrwufd296b62016-06-22 17:43:02 -0700408 log.debug("Netconf device {} removed from Netconf subController", deviceId);
409 } else {
410 log.warn("Netconf device {} does not exist in the store, " +
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530411 "it may already have been removed", deviceId);
helenyrwufd296b62016-06-22 17:43:02 -0700412 }
andreaeb70a942015-10-16 21:34:46 -0700413 }
414 }
415
andreaeb70a942015-10-16 21:34:46 -0700416 private void connectDevices() {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200417 Set<DeviceId> deviceSubjects =
418 cfgService.getSubjects(DeviceId.class, NetconfDeviceConfig.class);
419 deviceSubjects.forEach(deviceId -> {
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700420 connectDevice(cfgService.getConfig(deviceId, NetconfDeviceConfig.class));
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200421 });
helenyrwufd296b62016-06-22 17:43:02 -0700422 }
423
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700424
425 private void connectDevice(NetconfDeviceConfig config) {
426 if (config == null) {
427 return;
428 }
429 DeviceId deviceId = config.subject();
430 if (!deviceId.uri().getScheme().equals(SCHEME_NAME)) {
431 // not under my scheme, skipping
432 log.trace("{} not my scheme, skipping", deviceId);
433 return;
434 }
435 DeviceDescription deviceDescription = createDeviceRepresentation(deviceId, config);
436 log.debug("Connecting NETCONF device {}, on {}:{} with username {}",
437 deviceId, config.ip(), config.port(), config.username());
438 storeDeviceKey(config.sshKey(), config.username(), config.password(), deviceId);
mskala832d0472017-06-09 16:31:42 +0200439 retriedPortDiscoveryMap.putIfAbsent(deviceId, new AtomicInteger(0));
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700440 if (deviceService.getDevice(deviceId) == null) {
441 providerService.deviceConnected(deviceId, deviceDescription);
442 }
443 try {
444 checkAndUpdateDevice(deviceId, deviceDescription);
445 } catch (Exception e) {
446 log.error("Unhandled exception checking {}", deviceId, e);
447 }
448 }
449
helenyrwufd296b62016-06-22 17:43:02 -0700450 private void checkAndUpdateDevice(DeviceId deviceId, DeviceDescription deviceDescription) {
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530451 Device device = deviceService.getDevice(deviceId);
452 if (device == null) {
Andrea Campanella7bbe7b12017-05-03 16:03:38 -0700453 log.debug("Device {} has not been added to store, " +
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200454 "since it's not reachable", deviceId);
helenyrwufd296b62016-06-22 17:43:02 -0700455 } else {
456 boolean isReachable = isReachable(deviceId);
457 if (isReachable && !deviceService.isAvailable(deviceId)) {
Konstantinos Kanonakis4d67dd82016-08-05 12:18:52 -0500458 if (device.is(DeviceDescriptionDiscovery.class)) {
459 if (mastershipService.isLocalMaster(deviceId)) {
460 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
461 device.as(DeviceDescriptionDiscovery.class);
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200462 DeviceDescription updatedDeviceDescription =
463 deviceDescriptionDiscovery.discoverDeviceDetails();
Michele Santuari00cc1f72016-09-08 17:05:24 +0200464 if (updatedDeviceDescription != null &&
465 !descriptionEquals(device, updatedDeviceDescription)) {
466 providerService.deviceConnected(
467 deviceId, new DefaultDeviceDescription(
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200468 updatedDeviceDescription, true,
469 updatedDeviceDescription.annotations()));
Michele Santuari576f09c2016-09-28 14:20:00 +0200470 } else if (updatedDeviceDescription == null) {
471 providerService.deviceConnected(
472 deviceId, new DefaultDeviceDescription(
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200473 deviceDescription, true,
474 deviceDescription.annotations()));
Michele Santuari00cc1f72016-09-08 17:05:24 +0200475 }
Konstantinos Kanonakis4d67dd82016-08-05 12:18:52 -0500476 }
477 } else {
Michele Santuari576f09c2016-09-28 14:20:00 +0200478 log.warn("No DeviceDescriptionDiscovery behaviour for device {} " +
479 "using DefaultDeviceDescription", deviceId);
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530480 providerService.deviceConnected(
481 deviceId, new DefaultDeviceDescription(
Michele Santuari576f09c2016-09-28 14:20:00 +0200482 deviceDescription, true, deviceDescription.annotations()));
Konstantinos Kanonakis4d67dd82016-08-05 12:18:52 -0500483 }
helenyrwufd296b62016-06-22 17:43:02 -0700484 } else if (!isReachable && deviceService.isAvailable(deviceId)) {
485 providerService.deviceDisconnected(deviceId);
Vidyashree Rama229554a2017-04-14 15:24:45 +0530486 } else if (isReachable && deviceService.isAvailable(deviceId) &&
487 mastershipService.isLocalMaster(deviceId)) {
mskala832d0472017-06-09 16:31:42 +0200488
489 //if ports are not discovered, retry the discovery
490 if (deviceService.getPorts(deviceId).isEmpty() &&
491 retriedPortDiscoveryMap.get(deviceId).getAndIncrement() < maxRetries) {
492 discoverPorts(deviceId);
493 }
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530494 updatePortStatistics(device);
helenyrwufd296b62016-06-22 17:43:02 -0700495 }
496 }
497 }
498
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530499 private void updatePortStatistics(Device device) {
500 if (device.is(PortStatisticsDiscovery.class)) {
501 PortStatisticsDiscovery d = device.as(PortStatisticsDiscovery.class);
Andrea Campanellac3627842017-04-04 18:06:54 +0200502 Collection<PortStatistics> portStatistics = d.discoverPortStatistics();
503 if (portStatistics != null) {
504 providerService.updatePortStatistics(device.id(),
505 portStatistics);
506 }
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530507 } else {
Yuta HIGUCHIe4cb8cf2017-05-01 22:18:28 -0700508 log.debug("No port statistics getter behaviour for device {}",
509 device.id());
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530510 }
511 }
512
Michele Santuari00cc1f72016-09-08 17:05:24 +0200513 private boolean descriptionEquals(Device device, DeviceDescription updatedDeviceDescription) {
Yuta HIGUCHIf381fc72017-01-03 10:39:36 -0800514 return Objects.equal(device.id().uri(), updatedDeviceDescription.deviceUri())
Michele Santuari00cc1f72016-09-08 17:05:24 +0200515 && Objects.equal(device.type(), updatedDeviceDescription.type())
516 && Objects.equal(device.manufacturer(), updatedDeviceDescription.manufacturer())
517 && Objects.equal(device.hwVersion(), updatedDeviceDescription.hwVersion())
518 && Objects.equal(device.swVersion(), updatedDeviceDescription.swVersion())
519 && Objects.equal(device.serialNumber(), updatedDeviceDescription.serialNumber())
520 && Objects.equal(device.chassisId(), updatedDeviceDescription.chassisId())
521 && Objects.equal(device.annotations(), updatedDeviceDescription.annotations());
522 }
523
helenyrwufd296b62016-06-22 17:43:02 -0700524 private void checkAndUpdateDevices() {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200525 Set<DeviceId> deviceSubjects =
526 cfgService.getSubjects(DeviceId.class, NetconfDeviceConfig.class);
527 deviceSubjects.forEach(deviceId -> {
528 NetconfDeviceConfig config =
529 cfgService.getConfig(deviceId, NetconfDeviceConfig.class);
530 DeviceDescription deviceDescription = createDeviceRepresentation(deviceId, config);
531 storeDeviceKey(config.sshKey(), config.username(), config.password(), deviceId);
532 checkAndUpdateDevice(deviceId, deviceDescription);
533 });
andreaeb70a942015-10-16 21:34:46 -0700534 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530535
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200536 private DeviceDescription createDeviceRepresentation(DeviceId deviceId, NetconfDeviceConfig config) {
537 Preconditions.checkNotNull(deviceId, ISNULL);
538 //Netconf configuration object
539 ChassisId cid = new ChassisId();
540 String ipAddress = config.ip().toString();
541 SparseAnnotations annotations = DefaultAnnotations.builder()
542 .set(IPADDRESS, ipAddress)
543 .set(PORT, String.valueOf(config.port()))
544 .set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase())
545 .build();
546 return new DefaultDeviceDescription(
547 deviceId.uri(),
548 Device.Type.SWITCH,
549 UNKNOWN, UNKNOWN,
550 UNKNOWN, UNKNOWN,
551 cid, false,
552 annotations);
553 }
554
555 private void storeDeviceKey(String sshKey, String username, String password, DeviceId deviceId) {
556 if (sshKey.equals("")) {
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600557 deviceKeyAdminService.addKey(
558 DeviceKey.createDeviceKeyUsingUsernamePassword(
559 DeviceKeyId.deviceKeyId(deviceId.toString()),
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200560 null, username, password));
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600561 } else {
562 deviceKeyAdminService.addKey(
563 DeviceKey.createDeviceKeyUsingSshKey(
564 DeviceKeyId.deviceKeyId(deviceId.toString()),
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200565 null, username, password,
566 sshKey));
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600567 }
568 }
569
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700570 private void initiateConnection(DeviceId deviceId, MastershipRole newRole) {
571 try {
572 if (isReachable(deviceId)) {
573 controller.connectDevice(deviceId);
574 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.MASTER);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700575 }
576 } catch (Exception e) {
577 if (deviceService.getDevice(deviceId) != null) {
578 providerService.deviceDisconnected(deviceId);
579 }
580 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(deviceId.toString()));
581 throw new RuntimeException(new NetconfException(
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200582 "Can't connect to NETCONF device " + deviceId, e));
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700583
584 }
585 }
586
587 private void discoverPorts(DeviceId deviceId) {
588 Device device = deviceService.getDevice(deviceId);
Andrea Campanella6c71a052016-04-22 11:56:31 -0700589 //TODO remove when PortDiscovery is removed from master
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700590 if (device.is(PortDiscovery.class)) {
591 PortDiscovery portConfig = device.as(PortDiscovery.class);
592 providerService.updatePorts(deviceId,
593 portConfig.getPorts());
Andrea Campanella6c71a052016-04-22 11:56:31 -0700594 } else if (device.is(DeviceDescriptionDiscovery.class)) {
595 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
596 device.as(DeviceDescriptionDiscovery.class);
597 providerService.updatePorts(deviceId,
598 deviceDescriptionDiscovery.discoverPortDetails());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700599 } else {
600 log.warn("No portGetter behaviour for device {}", deviceId);
601 }
Gaurav Agrawaldab4d772017-03-29 15:15:13 +0530602
603 // Port statistics discovery
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530604 updatePortStatistics(device);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700605 }
606
607 /**
608 * Return the DeviceId about the device containing the URI.
609 *
Andrea Campanella6c71a052016-04-22 11:56:31 -0700610 * @param ip IP address
Ray Milkeyd4334db2016-04-05 17:39:44 -0700611 * @param port port number
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700612 * @return DeviceId
613 */
614 public DeviceId getDeviceId(String ip, int port) {
615 try {
616 return DeviceId.deviceId(new URI(NETCONF, ip + ":" + port, null));
617 } catch (URISyntaxException e) {
618 throw new IllegalArgumentException("Unable to build deviceID for device "
619 + ip + ":" + port, e);
620 }
621 }
622
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200623
624 protected void translateConfig() {
625 NetconfProviderConfig cfg = cfgService.getConfig(appId, NetconfProviderConfig.class);
626 if (cfg != null) {
627 try {
628 cfg.getDevicesAddresses().forEach(addr -> {
629 DeviceId deviceId = getDeviceId(addr.ip().toString(), addr.port());
630 log.info("Translating config for device {}", deviceId);
631 if (cfgService.getConfig(deviceId, NetconfDeviceConfig.class) == null) {
632 ObjectMapper mapper = new ObjectMapper();
633 ObjectNode device = mapper.createObjectNode();
634 device.put("ip", addr.ip().toString());
635 device.put("port", addr.port());
636 device.put("username", addr.name());
637 device.put("password", addr.password());
638 device.put("sshkey", addr.sshkey());
639 cfgService.applyConfig(deviceId, NetconfDeviceConfig.class, device);
640 } else {
641 // This is a corner case where new updated config is
642 // pushed with old /app tree after an initial with the
643 // new device/ tree. Since old method will be deprecated
644 // it's ok to ignore
645 log.warn("Config for device {} already exists, ignoring", deviceId);
646 }
647
648 });
649 } catch (ConfigException e) {
650 log.error("Cannot read config error " + e);
651 }
652 }
653 }
654
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700655 /**
656 * Listener for configuration events.
657 */
andreaeb70a942015-10-16 21:34:46 -0700658 private class InternalNetworkConfigListener implements NetworkConfigListener {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530659
andreaeb70a942015-10-16 21:34:46 -0700660
661 @Override
662 public void event(NetworkConfigEvent event) {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200663 if (event.configClass().equals(NetconfDeviceConfig.class)) {
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700664 executor.execute(() -> connectDevice((NetconfDeviceConfig) event.config().get()));
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200665 } else {
666 log.warn("Injecting device via this Json is deprecated, " +
667 "please put configuration under devices/ as shown in the wiki");
668 translateConfig();
669 }
670
Sanjay Se8dcfee2015-04-23 10:07:08 +0530671 }
672
andreaeb70a942015-10-16 21:34:46 -0700673 @Override
674 public boolean isRelevant(NetworkConfigEvent event) {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200675 return (event.configClass().equals(NetconfDeviceConfig.class) ||
676 event.configClass().equals(NetconfProviderConfig.class)) &&
andreaeb70a942015-10-16 21:34:46 -0700677 (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
678 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530679 }
680 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700681
682 /**
683 * Listener for core device events.
684 */
685 private class InternalDeviceListener implements DeviceListener {
686 @Override
687 public void event(DeviceEvent event) {
688 if ((event.type() == DeviceEvent.Type.DEVICE_ADDED)) {
689 executor.execute(() -> discoverPorts(event.subject().id()));
690 } else if ((event.type() == DeviceEvent.Type.DEVICE_REMOVED)) {
691 log.debug("removing device {}", event.subject().id());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700692 controller.disconnectDevice(event.subject().id(), true);
693 }
694 }
695
696 @Override
697 public boolean isRelevant(DeviceEvent event) {
698 if (mastershipService.getMasterFor(event.subject().id()) == null) {
699 return true;
700 }
Andrea Campanella968f93f2017-06-08 11:09:28 +0200701 return (SCHEME_NAME.equalsIgnoreCase(event.subject().annotations().value(AnnotationKeys.PROTOCOL)) ||
702 (SCHEME_NAME.equalsIgnoreCase(event.subject().id().uri().getScheme()))) &&
Michele Santuari576f09c2016-09-28 14:20:00 +0200703 mastershipService.isLocalMaster(event.subject().id());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700704 }
705 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530706}