blob: 5ddbba099adf74b79314ccbccf3e188a6ddac049 [file] [log] [blame]
Sanjay Se8dcfee2015-04-23 10:07:08 +05301/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
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;
36import org.onosproject.incubator.net.config.basics.ConfigException;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070037import org.onosproject.mastership.MastershipService;
Marc De Leenheerb0d131c2016-03-01 20:34:59 -080038import org.onosproject.net.AnnotationKeys;
andreaeb70a942015-10-16 21:34:46 -070039import org.onosproject.net.DefaultAnnotations;
Sanjay Se8dcfee2015-04-23 10:07:08 +053040import org.onosproject.net.Device;
41import org.onosproject.net.DeviceId;
42import org.onosproject.net.MastershipRole;
Saurav Dasa2d37502016-03-25 17:50:40 -070043import org.onosproject.net.PortNumber;
andreaeb70a942015-10-16 21:34:46 -070044import org.onosproject.net.SparseAnnotations;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -080045import org.onosproject.net.behaviour.PortDiscovery;
andreaeb70a942015-10-16 21:34:46 -070046import org.onosproject.net.config.ConfigFactory;
47import org.onosproject.net.config.NetworkConfigEvent;
48import org.onosproject.net.config.NetworkConfigListener;
49import org.onosproject.net.config.NetworkConfigRegistry;
Andrea Campanella34cf65c2017-04-12 13:51:32 +020050import org.onosproject.net.config.basics.SubjectFactories;
Sanjay Se8dcfee2015-04-23 10:07:08 +053051import org.onosproject.net.device.DefaultDeviceDescription;
52import org.onosproject.net.device.DeviceDescription;
Andrea Campanella6c71a052016-04-22 11:56:31 -070053import org.onosproject.net.device.DeviceDescriptionDiscovery;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070054import org.onosproject.net.device.DeviceEvent;
55import org.onosproject.net.device.DeviceListener;
Sanjay Se8dcfee2015-04-23 10:07:08 +053056import org.onosproject.net.device.DeviceProvider;
57import org.onosproject.net.device.DeviceProviderRegistry;
58import org.onosproject.net.device.DeviceProviderService;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -080059import org.onosproject.net.device.DeviceService;
Andrea Campanellac3627842017-04-04 18:06:54 +020060import org.onosproject.net.device.PortStatistics;
Gaurav Agrawaldab4d772017-03-29 15:15:13 +053061import org.onosproject.net.device.PortStatisticsDiscovery;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070062import org.onosproject.net.key.DeviceKey;
63import org.onosproject.net.key.DeviceKeyAdminService;
64import org.onosproject.net.key.DeviceKeyId;
Sanjay Se8dcfee2015-04-23 10:07:08 +053065import org.onosproject.net.provider.AbstractProvider;
66import org.onosproject.net.provider.ProviderId;
andreaeb70a942015-10-16 21:34:46 -070067import org.onosproject.netconf.NetconfController;
andreaeb70a942015-10-16 21:34:46 -070068import org.onosproject.netconf.NetconfDeviceListener;
Andrea Campanella8b1cb672016-01-25 13:58:58 -080069import org.onosproject.netconf.NetconfException;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -070070import org.osgi.service.component.ComponentContext;
Sanjay Se8dcfee2015-04-23 10:07:08 +053071import org.slf4j.Logger;
72
Andrea Campanella087ceb92015-12-07 09:58:34 -080073import java.io.IOException;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070074import java.net.Socket;
75import java.net.URI;
76import java.net.URISyntaxException;
77import java.util.Arrays;
Andrea Campanellac3627842017-04-04 18:06:54 +020078import java.util.Collection;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -070079import java.util.Dictionary;
Andrea Campanella34cf65c2017-04-12 13:51:32 +020080import java.util.List;
81import java.util.Set;
Andrea Campanella5c999e22016-03-01 15:12:53 -080082import java.util.concurrent.ExecutorService;
83import java.util.concurrent.Executors;
helenyrwufd296b62016-06-22 17:43:02 -070084import java.util.concurrent.ScheduledExecutorService;
85import java.util.concurrent.ScheduledFuture;
86import java.util.concurrent.TimeUnit;
andreaeb70a942015-10-16 21:34:46 -070087
Yuta HIGUCHI1624df12016-07-21 16:54:33 -070088import static java.util.concurrent.Executors.newScheduledThreadPool;
Andrea Campanella5c999e22016-03-01 15:12:53 -080089import static org.onlab.util.Tools.groupedThreads;
andreaeb70a942015-10-16 21:34:46 -070090import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
91import static org.slf4j.LoggerFactory.getLogger;
92
Sanjay Se8dcfee2015-04-23 10:07:08 +053093/**
andreaeb70a942015-10-16 21:34:46 -070094 * Provider which uses an NETCONF controller to detect device.
Sanjay Se8dcfee2015-04-23 10:07:08 +053095 */
96@Component(immediate = true)
97public class NetconfDeviceProvider extends AbstractProvider
98 implements DeviceProvider {
Andrea Campanella7e6200a2016-03-21 09:48:40 -070099
andreaeb70a942015-10-16 21:34:46 -0700100 private final Logger log = getLogger(getClass());
Sanjay Se8dcfee2015-04-23 10:07:08 +0530101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected DeviceProviderRegistry providerRegistry;
104
andreaeb70a942015-10-16 21:34:46 -0700105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella101417d2015-12-11 17:58:07 -0800106 protected NetconfController controller;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
andreaeb70a942015-10-16 21:34:46 -0700109 protected NetworkConfigRegistry cfgService;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530110
Thomas Vachuskad6811712015-04-29 21:37:04 -0700111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
andreaeb70a942015-10-16 21:34:46 -0700112 protected CoreService coreService;
Thomas Vachuskad6811712015-04-29 21:37:04 -0700113
Aaron Kruglikov17b4c852016-01-15 16:37:04 -0800114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700115 protected DeviceService deviceService;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -0800116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700118 protected DeviceKeyAdminService deviceKeyAdminService;
119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121 protected MastershipService mastershipService;
122
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
124 protected ComponentConfigService componentConfigService;
125
126
Michele Santuari576f09c2016-09-28 14:20:00 +0200127 protected static final String APP_NAME = "org.onosproject.netconf";
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200128 protected static final String SCHEME_NAME = "netconf";
Andrea Campanella101417d2015-12-11 17:58:07 -0800129 private static final String DEVICE_PROVIDER_PACKAGE = "org.onosproject.netconf.provider.device";
130 private static final String UNKNOWN = "unknown";
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700131 protected static final String ISNULL = "NetconfDeviceInfo is null";
132 private static final String IPADDRESS = "ipaddress";
133 private static final String NETCONF = "netconf";
134 private static final String PORT = "port";
helenyrwufd296b62016-06-22 17:43:02 -0700135 private static final int CORE_POOL_SIZE = 10;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700136
Thomas Vachuskadb29dcf2017-03-31 11:26:19 -0700137 private static final int DEFAULT_POLL_FREQUENCY_SECONDS = 30;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700138 @Property(name = "pollFrequency", intValue = DEFAULT_POLL_FREQUENCY_SECONDS,
139 label = "Configure poll frequency for port status and statistics; " +
Thomas Vachuskadb29dcf2017-03-31 11:26:19 -0700140 "default is 30 sec")
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700141 private int pollFrequency = DEFAULT_POLL_FREQUENCY_SECONDS;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530142
Michele Santuari576f09c2016-09-28 14:20:00 +0200143 protected final ExecutorService executor =
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700144 Executors.newFixedThreadPool(5, groupedThreads("onos/netconfdeviceprovider",
145 "device-installer-%d", log));
Yuta HIGUCHI1624df12016-07-21 16:54:33 -0700146 protected ScheduledExecutorService connectionExecutor
147 = newScheduledThreadPool(CORE_POOL_SIZE,
148 groupedThreads("onos/netconfdeviceprovider",
149 "connection-executor-%d", log));
Andrea Campanella5c999e22016-03-01 15:12:53 -0800150
Michele Santuari576f09c2016-09-28 14:20:00 +0200151 protected DeviceProviderService providerService;
andreaeb70a942015-10-16 21:34:46 -0700152 private NetconfDeviceListener innerNodeListener = new InnerNetconfDeviceListener();
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700153 private InternalDeviceListener deviceListener = new InternalDeviceListener();
Michele Santuari576f09c2016-09-28 14:20:00 +0200154 protected ScheduledFuture<?> scheduledTask;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530155
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200156 protected final List<ConfigFactory> factories = ImmutableList.of(
157 new ConfigFactory<DeviceId, NetconfDeviceConfig>(
158 SubjectFactories.DEVICE_SUBJECT_FACTORY,
159 NetconfDeviceConfig.class, SCHEME_NAME) {
160 @Override
161 public NetconfDeviceConfig createConfig() {
162 return new NetconfDeviceConfig();
163 }
164 },
andreaeb70a942015-10-16 21:34:46 -0700165 new ConfigFactory<ApplicationId, NetconfProviderConfig>(APP_SUBJECT_FACTORY,
166 NetconfProviderConfig.class,
167 "devices",
168 true) {
169 @Override
170 public NetconfProviderConfig createConfig() {
171 return new NetconfProviderConfig();
172 }
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200173 });
174
Michele Santuari576f09c2016-09-28 14:20:00 +0200175 protected final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
andreaeb70a942015-10-16 21:34:46 -0700176 private ApplicationId appId;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700177 private boolean active;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530178
Sanjay Se8dcfee2015-04-23 10:07:08 +0530179
180 @Activate
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700181 public void activate(ComponentContext context) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700182 active = true;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700183 componentConfigService.registerProperties(getClass());
Sanjay Se8dcfee2015-04-23 10:07:08 +0530184 providerService = providerRegistry.register(this);
Andrea Campanella101417d2015-12-11 17:58:07 -0800185 appId = coreService.registerApplication(APP_NAME);
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200186 factories.forEach(cfgService::registerConfigFactory);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700187 cfgService.addListener(cfgListener);
andreaeb70a942015-10-16 21:34:46 -0700188 controller.addDeviceListener(innerNodeListener);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700189 deviceService.addListener(deviceListener);
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200190 translateConfig();
Andrea Campanella7d8449b2016-03-02 10:16:42 -0800191 executor.execute(NetconfDeviceProvider.this::connectDevices);
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700192 modified(context);
Thomas Vachuskad6811712015-04-29 21:37:04 -0700193 log.info("Started");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530194 }
195
andreaeb70a942015-10-16 21:34:46 -0700196
Sanjay Se8dcfee2015-04-23 10:07:08 +0530197 @Deactivate
andreaeb70a942015-10-16 21:34:46 -0700198 public void deactivate() {
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700199 componentConfigService.unregisterProperties(getClass(), false);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700200 deviceService.removeListener(deviceListener);
201 active = false;
202 controller.getNetconfDevices().forEach(id -> {
203 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(id.toString()));
204 controller.disconnectDevice(id, true);
205 });
Andrea Campanella86294db2016-03-07 11:42:49 -0800206 controller.removeDeviceListener(innerNodeListener);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700207 deviceService.removeListener(deviceListener);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530208 providerRegistry.unregister(this);
209 providerService = null;
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200210 factories.forEach(cfgService::unregisterConfigFactory);
helenyrwufd296b62016-06-22 17:43:02 -0700211 scheduledTask.cancel(true);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700212 executor.shutdown();
Sanjay Seb5eebb2015-04-24 15:44:50 +0530213 log.info("Stopped");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530214 }
215
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700216
217 @Modified
218 public void modified(ComponentContext context) {
219 if (context != null) {
220 Dictionary<?, ?> properties = context.getProperties();
221 pollFrequency = Tools.getIntegerProperty(properties, "pollFrequency",
222 DEFAULT_POLL_FREQUENCY_SECONDS);
223 log.info("Configured. Poll frequency is configured to {} seconds", pollFrequency);
224 }
225 if (scheduledTask != null) {
226 scheduledTask.cancel(false);
227 }
228 scheduledTask = schedulePolling();
229 }
230
andreaeb70a942015-10-16 21:34:46 -0700231 public NetconfDeviceProvider() {
Andrea Campanella101417d2015-12-11 17:58:07 -0800232 super(new ProviderId(SCHEME_NAME, DEVICE_PROVIDER_PACKAGE));
Sanjay Se8dcfee2015-04-23 10:07:08 +0530233 }
234
helenyrwufd296b62016-06-22 17:43:02 -0700235 // Checks connection to devices in the config file
236 // every DEFAULT_POLL_FREQUENCY_SECONDS seconds.
237 private ScheduledFuture schedulePolling() {
Yuta HIGUCHIc2b82e32017-03-10 14:33:41 -0800238 return connectionExecutor.scheduleAtFixedRate(exceptionSafe(this::checkAndUpdateDevices),
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700239 pollFrequency / 10,
240 pollFrequency, TimeUnit.SECONDS);
helenyrwufd296b62016-06-22 17:43:02 -0700241 }
242
Yuta HIGUCHIc2b82e32017-03-10 14:33:41 -0800243 private Runnable exceptionSafe(Runnable runnable) {
244 return new Runnable() {
245
246 @Override
247 public void run() {
248 try {
249 runnable.run();
250 } catch (Exception e) {
251 log.error("Unhandled Exception", e);
252 }
253 }
254 };
255 }
256
Sanjay Se8dcfee2015-04-23 10:07:08 +0530257 @Override
258 public void triggerProbe(DeviceId deviceId) {
andreaeb70a942015-10-16 21:34:46 -0700259 // TODO: This will be implemented later.
Yuta HIGUCHIe4cb8cf2017-05-01 22:18:28 -0700260 log.debug("Should be triggering probe on device {}", deviceId);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530261 }
262
263 @Override
264 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700265 if (active) {
266 switch (newRole) {
267 case MASTER:
268 initiateConnection(deviceId, newRole);
269 log.debug("Accepting mastership role change to {} for device {}", newRole, deviceId);
270 break;
271 case STANDBY:
272 controller.disconnectDevice(deviceId, false);
273 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.STANDBY);
274 //else no-op
275 break;
276 case NONE:
277 controller.disconnectDevice(deviceId, false);
278 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
279 break;
280 default:
281 log.error("Unimplemented Mastership state : {}", newRole);
282
283 }
284 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530285 }
286
287 @Override
288 public boolean isReachable(DeviceId deviceId) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700289 //FIXME this is a workaround util device state is shared
290 // between controller instances.
291 Device device = deviceService.getDevice(deviceId);
292 String ip;
293 int port;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700294 if (device != null) {
295 ip = device.annotations().value(IPADDRESS);
296 port = Integer.parseInt(device.annotations().value(PORT));
297 } else {
298 String[] info = deviceId.toString().split(":");
299 if (info.length == 3) {
300 ip = info[1];
301 port = Integer.parseInt(info[2]);
302 } else {
303 ip = Arrays.asList(info).stream().filter(el -> !el.equals(info[0])
304 && !el.equals(info[info.length - 1]))
305 .reduce((t, u) -> t + ":" + u)
306 .get();
307 log.debug("ip v6 {}", ip);
308 port = Integer.parseInt(info[info.length - 1]);
309 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530310 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700311 //test connection to device opening a socket to it.
Yuta HIGUCHI70c21472017-04-20 20:40:46 -0700312 try (Socket socket = new Socket(ip, port)) {
Yuta HIGUCHI0454d702017-03-17 10:08:38 -0700313 log.debug("rechability of {}, {}, {}", deviceId, socket.isConnected(), !socket.isClosed());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700314 return socket.isConnected() && !socket.isClosed();
315 } catch (IOException e) {
316 log.info("Device {} is not reachable", deviceId);
317 return false;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700318 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530319 }
320
Saurav Dasa2d37502016-03-25 17:50:40 -0700321 @Override
322 public void changePortState(DeviceId deviceId, PortNumber portNumber,
323 boolean enable) {
324 // TODO if required
325 }
326
andreaeb70a942015-10-16 21:34:46 -0700327 private class InnerNetconfDeviceListener implements NetconfDeviceListener {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530328
Andrea Campanella101417d2015-12-11 17:58:07 -0800329
andreaeb70a942015-10-16 21:34:46 -0700330 @Override
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700331 public void deviceAdded(DeviceId deviceId) {
332 //no-op
333 log.debug("Netconf device {} added to Netconf subController", deviceId);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530334 }
335
336 @Override
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700337 public void deviceRemoved(DeviceId deviceId) {
338 Preconditions.checkNotNull(deviceId, ISNULL);
helenyrwufd296b62016-06-22 17:43:02 -0700339
340 if (deviceService.getDevice(deviceId) != null) {
341 providerService.deviceDisconnected(deviceId);
342 log.debug("Netconf device {} removed from Netconf subController", deviceId);
343 } else {
344 log.warn("Netconf device {} does not exist in the store, " +
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530345 "it may already have been removed", deviceId);
helenyrwufd296b62016-06-22 17:43:02 -0700346 }
andreaeb70a942015-10-16 21:34:46 -0700347 }
348 }
349
andreaeb70a942015-10-16 21:34:46 -0700350 private void connectDevices() {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200351 Set<DeviceId> deviceSubjects =
352 cfgService.getSubjects(DeviceId.class, NetconfDeviceConfig.class);
353 deviceSubjects.forEach(deviceId -> {
354 NetconfDeviceConfig config =
355 cfgService.getConfig(deviceId, NetconfDeviceConfig.class);
356 DeviceDescription deviceDescription = createDeviceRepresentation(deviceId, config);
357 log.debug("Connecting NETCONF device {}, on {}:{} with username {}",
358 deviceId, config.ip(), config.port(), config.username());
359 storeDeviceKey(config.sshKey(), config.username(), config.password(), deviceId);
360 if (deviceService.getDevice(deviceId) == null) {
361 providerService.deviceConnected(deviceId, deviceDescription);
helenyrwufd296b62016-06-22 17:43:02 -0700362 }
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200363 try {
364 checkAndUpdateDevice(deviceId, deviceDescription);
365 } catch (Exception e) {
366 log.error("Unhandled exception checking {}", deviceId, e);
367 }
368 });
helenyrwufd296b62016-06-22 17:43:02 -0700369 }
370
371 private void checkAndUpdateDevice(DeviceId deviceId, DeviceDescription deviceDescription) {
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530372 Device device = deviceService.getDevice(deviceId);
373 if (device == null) {
helenyrwufd296b62016-06-22 17:43:02 -0700374 log.warn("Device {} has not been added to store, " +
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200375 "since it's not reachable", deviceId);
helenyrwufd296b62016-06-22 17:43:02 -0700376 } else {
377 boolean isReachable = isReachable(deviceId);
378 if (isReachable && !deviceService.isAvailable(deviceId)) {
Konstantinos Kanonakis4d67dd82016-08-05 12:18:52 -0500379 if (device.is(DeviceDescriptionDiscovery.class)) {
380 if (mastershipService.isLocalMaster(deviceId)) {
381 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
382 device.as(DeviceDescriptionDiscovery.class);
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200383 DeviceDescription updatedDeviceDescription =
384 deviceDescriptionDiscovery.discoverDeviceDetails();
Michele Santuari00cc1f72016-09-08 17:05:24 +0200385 if (updatedDeviceDescription != null &&
386 !descriptionEquals(device, updatedDeviceDescription)) {
387 providerService.deviceConnected(
388 deviceId, new DefaultDeviceDescription(
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200389 updatedDeviceDescription, true,
390 updatedDeviceDescription.annotations()));
Michele Santuari576f09c2016-09-28 14:20:00 +0200391 } else if (updatedDeviceDescription == null) {
392 providerService.deviceConnected(
393 deviceId, new DefaultDeviceDescription(
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200394 deviceDescription, true,
395 deviceDescription.annotations()));
Michele Santuari00cc1f72016-09-08 17:05:24 +0200396 }
397 //if ports are not discovered, retry the discovery
398 if (deviceService.getPorts(deviceId).isEmpty()) {
399 discoverPorts(deviceId);
400 }
Konstantinos Kanonakis4d67dd82016-08-05 12:18:52 -0500401 }
402 } else {
Michele Santuari576f09c2016-09-28 14:20:00 +0200403 log.warn("No DeviceDescriptionDiscovery behaviour for device {} " +
404 "using DefaultDeviceDescription", deviceId);
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530405 providerService.deviceConnected(
406 deviceId, new DefaultDeviceDescription(
Michele Santuari576f09c2016-09-28 14:20:00 +0200407 deviceDescription, true, deviceDescription.annotations()));
Konstantinos Kanonakis4d67dd82016-08-05 12:18:52 -0500408 }
helenyrwufd296b62016-06-22 17:43:02 -0700409 } else if (!isReachable && deviceService.isAvailable(deviceId)) {
410 providerService.deviceDisconnected(deviceId);
Vidyashree Rama229554a2017-04-14 15:24:45 +0530411 } else if (isReachable && deviceService.isAvailable(deviceId) &&
412 mastershipService.isLocalMaster(deviceId)) {
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530413 updatePortStatistics(device);
helenyrwufd296b62016-06-22 17:43:02 -0700414 }
415 }
416 }
417
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530418 private void updatePortStatistics(Device device) {
419 if (device.is(PortStatisticsDiscovery.class)) {
420 PortStatisticsDiscovery d = device.as(PortStatisticsDiscovery.class);
Andrea Campanellac3627842017-04-04 18:06:54 +0200421 Collection<PortStatistics> portStatistics = d.discoverPortStatistics();
422 if (portStatistics != null) {
423 providerService.updatePortStatistics(device.id(),
424 portStatistics);
425 }
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530426 } else {
Yuta HIGUCHIe4cb8cf2017-05-01 22:18:28 -0700427 log.debug("No port statistics getter behaviour for device {}",
428 device.id());
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530429 }
430 }
431
Michele Santuari00cc1f72016-09-08 17:05:24 +0200432 private boolean descriptionEquals(Device device, DeviceDescription updatedDeviceDescription) {
Yuta HIGUCHIf381fc72017-01-03 10:39:36 -0800433 return Objects.equal(device.id().uri(), updatedDeviceDescription.deviceUri())
Michele Santuari00cc1f72016-09-08 17:05:24 +0200434 && Objects.equal(device.type(), updatedDeviceDescription.type())
435 && Objects.equal(device.manufacturer(), updatedDeviceDescription.manufacturer())
436 && Objects.equal(device.hwVersion(), updatedDeviceDescription.hwVersion())
437 && Objects.equal(device.swVersion(), updatedDeviceDescription.swVersion())
438 && Objects.equal(device.serialNumber(), updatedDeviceDescription.serialNumber())
439 && Objects.equal(device.chassisId(), updatedDeviceDescription.chassisId())
440 && Objects.equal(device.annotations(), updatedDeviceDescription.annotations());
441 }
442
helenyrwufd296b62016-06-22 17:43:02 -0700443 private void checkAndUpdateDevices() {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200444 Set<DeviceId> deviceSubjects =
445 cfgService.getSubjects(DeviceId.class, NetconfDeviceConfig.class);
446 deviceSubjects.forEach(deviceId -> {
447 NetconfDeviceConfig config =
448 cfgService.getConfig(deviceId, NetconfDeviceConfig.class);
449 DeviceDescription deviceDescription = createDeviceRepresentation(deviceId, config);
450 storeDeviceKey(config.sshKey(), config.username(), config.password(), deviceId);
451 checkAndUpdateDevice(deviceId, deviceDescription);
452 });
andreaeb70a942015-10-16 21:34:46 -0700453 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530454
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200455 private DeviceDescription createDeviceRepresentation(DeviceId deviceId, NetconfDeviceConfig config) {
456 Preconditions.checkNotNull(deviceId, ISNULL);
457 //Netconf configuration object
458 ChassisId cid = new ChassisId();
459 String ipAddress = config.ip().toString();
460 SparseAnnotations annotations = DefaultAnnotations.builder()
461 .set(IPADDRESS, ipAddress)
462 .set(PORT, String.valueOf(config.port()))
463 .set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase())
464 .build();
465 return new DefaultDeviceDescription(
466 deviceId.uri(),
467 Device.Type.SWITCH,
468 UNKNOWN, UNKNOWN,
469 UNKNOWN, UNKNOWN,
470 cid, false,
471 annotations);
472 }
473
474 private void storeDeviceKey(String sshKey, String username, String password, DeviceId deviceId) {
475 if (sshKey.equals("")) {
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600476 deviceKeyAdminService.addKey(
477 DeviceKey.createDeviceKeyUsingUsernamePassword(
478 DeviceKeyId.deviceKeyId(deviceId.toString()),
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200479 null, username, password));
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600480 } else {
481 deviceKeyAdminService.addKey(
482 DeviceKey.createDeviceKeyUsingSshKey(
483 DeviceKeyId.deviceKeyId(deviceId.toString()),
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200484 null, username, password,
485 sshKey));
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600486 }
487 }
488
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700489 private void initiateConnection(DeviceId deviceId, MastershipRole newRole) {
490 try {
491 if (isReachable(deviceId)) {
492 controller.connectDevice(deviceId);
493 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.MASTER);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700494 }
495 } catch (Exception e) {
496 if (deviceService.getDevice(deviceId) != null) {
497 providerService.deviceDisconnected(deviceId);
498 }
499 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(deviceId.toString()));
500 throw new RuntimeException(new NetconfException(
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200501 "Can't connect to NETCONF device " + deviceId, e));
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700502
503 }
504 }
505
506 private void discoverPorts(DeviceId deviceId) {
507 Device device = deviceService.getDevice(deviceId);
Andrea Campanella6c71a052016-04-22 11:56:31 -0700508 //TODO remove when PortDiscovery is removed from master
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700509 if (device.is(PortDiscovery.class)) {
510 PortDiscovery portConfig = device.as(PortDiscovery.class);
511 providerService.updatePorts(deviceId,
512 portConfig.getPorts());
Andrea Campanella6c71a052016-04-22 11:56:31 -0700513 } else if (device.is(DeviceDescriptionDiscovery.class)) {
514 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
515 device.as(DeviceDescriptionDiscovery.class);
516 providerService.updatePorts(deviceId,
517 deviceDescriptionDiscovery.discoverPortDetails());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700518 } else {
519 log.warn("No portGetter behaviour for device {}", deviceId);
520 }
Gaurav Agrawaldab4d772017-03-29 15:15:13 +0530521
522 // Port statistics discovery
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530523 updatePortStatistics(device);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700524 }
525
526 /**
527 * Return the DeviceId about the device containing the URI.
528 *
Andrea Campanella6c71a052016-04-22 11:56:31 -0700529 * @param ip IP address
Ray Milkeyd4334db2016-04-05 17:39:44 -0700530 * @param port port number
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700531 * @return DeviceId
532 */
533 public DeviceId getDeviceId(String ip, int port) {
534 try {
535 return DeviceId.deviceId(new URI(NETCONF, ip + ":" + port, null));
536 } catch (URISyntaxException e) {
537 throw new IllegalArgumentException("Unable to build deviceID for device "
538 + ip + ":" + port, e);
539 }
540 }
541
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200542
543 protected void translateConfig() {
544 NetconfProviderConfig cfg = cfgService.getConfig(appId, NetconfProviderConfig.class);
545 if (cfg != null) {
546 try {
547 cfg.getDevicesAddresses().forEach(addr -> {
548 DeviceId deviceId = getDeviceId(addr.ip().toString(), addr.port());
549 log.info("Translating config for device {}", deviceId);
550 if (cfgService.getConfig(deviceId, NetconfDeviceConfig.class) == null) {
551 ObjectMapper mapper = new ObjectMapper();
552 ObjectNode device = mapper.createObjectNode();
553 device.put("ip", addr.ip().toString());
554 device.put("port", addr.port());
555 device.put("username", addr.name());
556 device.put("password", addr.password());
557 device.put("sshkey", addr.sshkey());
558 cfgService.applyConfig(deviceId, NetconfDeviceConfig.class, device);
559 } else {
560 // This is a corner case where new updated config is
561 // pushed with old /app tree after an initial with the
562 // new device/ tree. Since old method will be deprecated
563 // it's ok to ignore
564 log.warn("Config for device {} already exists, ignoring", deviceId);
565 }
566
567 });
568 } catch (ConfigException e) {
569 log.error("Cannot read config error " + e);
570 }
571 }
572 }
573
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700574 /**
575 * Listener for configuration events.
576 */
andreaeb70a942015-10-16 21:34:46 -0700577 private class InternalNetworkConfigListener implements NetworkConfigListener {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530578
andreaeb70a942015-10-16 21:34:46 -0700579
580 @Override
581 public void event(NetworkConfigEvent event) {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200582 if (event.configClass().equals(NetconfDeviceConfig.class)) {
583 executor.execute(NetconfDeviceProvider.this::connectDevices);
584 } else {
585 log.warn("Injecting device via this Json is deprecated, " +
586 "please put configuration under devices/ as shown in the wiki");
587 translateConfig();
588 }
589
Sanjay Se8dcfee2015-04-23 10:07:08 +0530590 }
591
andreaeb70a942015-10-16 21:34:46 -0700592 @Override
593 public boolean isRelevant(NetworkConfigEvent event) {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200594 return (event.configClass().equals(NetconfDeviceConfig.class) ||
595 event.configClass().equals(NetconfProviderConfig.class)) &&
andreaeb70a942015-10-16 21:34:46 -0700596 (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
597 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530598 }
599 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700600
601 /**
602 * Listener for core device events.
603 */
604 private class InternalDeviceListener implements DeviceListener {
605 @Override
606 public void event(DeviceEvent event) {
607 if ((event.type() == DeviceEvent.Type.DEVICE_ADDED)) {
608 executor.execute(() -> discoverPorts(event.subject().id()));
609 } else if ((event.type() == DeviceEvent.Type.DEVICE_REMOVED)) {
610 log.debug("removing device {}", event.subject().id());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700611 controller.disconnectDevice(event.subject().id(), true);
612 }
613 }
614
615 @Override
616 public boolean isRelevant(DeviceEvent event) {
617 if (mastershipService.getMasterFor(event.subject().id()) == null) {
618 return true;
619 }
Yuta HIGUCHI8c6a7e82017-03-14 18:35:43 -0700620 return SCHEME_NAME.toUpperCase()
621 .equals(event.subject().annotations().value(AnnotationKeys.PROTOCOL)) &&
Michele Santuari576f09c2016-09-28 14:20:00 +0200622 mastershipService.isLocalMaster(event.subject().id());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700623 }
624 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530625}