blob: 72fc13f69df601cdcfe65299eb591d556daf51fb [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;
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -070070import org.onosproject.netconf.config.NetconfDeviceConfig;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -070071import org.osgi.service.component.ComponentContext;
Sanjay Se8dcfee2015-04-23 10:07:08 +053072import org.slf4j.Logger;
73
Andrea Campanella087ceb92015-12-07 09:58:34 -080074import java.io.IOException;
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;
Andrea Campanella34cf65c2017-04-12 13:51:32 +020081import java.util.List;
82import java.util.Set;
Andrea Campanella5c999e22016-03-01 15:12:53 -080083import java.util.concurrent.ExecutorService;
84import java.util.concurrent.Executors;
helenyrwufd296b62016-06-22 17:43:02 -070085import java.util.concurrent.ScheduledExecutorService;
86import java.util.concurrent.ScheduledFuture;
87import java.util.concurrent.TimeUnit;
andreaeb70a942015-10-16 21:34:46 -070088
Yuta HIGUCHI1624df12016-07-21 16:54:33 -070089import static java.util.concurrent.Executors.newScheduledThreadPool;
Andrea Campanella5c999e22016-03-01 15:12:53 -080090import static org.onlab.util.Tools.groupedThreads;
andreaeb70a942015-10-16 21:34:46 -070091import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
92import static org.slf4j.LoggerFactory.getLogger;
93
Sanjay Se8dcfee2015-04-23 10:07:08 +053094/**
andreaeb70a942015-10-16 21:34:46 -070095 * Provider which uses an NETCONF controller to detect device.
Sanjay Se8dcfee2015-04-23 10:07:08 +053096 */
97@Component(immediate = true)
98public class NetconfDeviceProvider extends AbstractProvider
99 implements DeviceProvider {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700100
andreaeb70a942015-10-16 21:34:46 -0700101 private final Logger log = getLogger(getClass());
Sanjay Se8dcfee2015-04-23 10:07:08 +0530102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
104 protected DeviceProviderRegistry providerRegistry;
105
andreaeb70a942015-10-16 21:34:46 -0700106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella101417d2015-12-11 17:58:07 -0800107 protected NetconfController controller;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
andreaeb70a942015-10-16 21:34:46 -0700110 protected NetworkConfigRegistry cfgService;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530111
Thomas Vachuskad6811712015-04-29 21:37:04 -0700112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
andreaeb70a942015-10-16 21:34:46 -0700113 protected CoreService coreService;
Thomas Vachuskad6811712015-04-29 21:37:04 -0700114
Aaron Kruglikov17b4c852016-01-15 16:37:04 -0800115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700116 protected DeviceService deviceService;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -0800117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700119 protected DeviceKeyAdminService deviceKeyAdminService;
120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
122 protected MastershipService mastershipService;
123
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
125 protected ComponentConfigService componentConfigService;
126
127
Michele Santuari576f09c2016-09-28 14:20:00 +0200128 protected static final String APP_NAME = "org.onosproject.netconf";
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200129 protected static final String SCHEME_NAME = "netconf";
Andrea Campanella101417d2015-12-11 17:58:07 -0800130 private static final String DEVICE_PROVIDER_PACKAGE = "org.onosproject.netconf.provider.device";
131 private static final String UNKNOWN = "unknown";
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700132 protected static final String ISNULL = "NetconfDeviceInfo is null";
133 private static final String IPADDRESS = "ipaddress";
134 private static final String NETCONF = "netconf";
135 private static final String PORT = "port";
helenyrwufd296b62016-06-22 17:43:02 -0700136 private static final int CORE_POOL_SIZE = 10;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700137
Thomas Vachuskadb29dcf2017-03-31 11:26:19 -0700138 private static final int DEFAULT_POLL_FREQUENCY_SECONDS = 30;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700139 @Property(name = "pollFrequency", intValue = DEFAULT_POLL_FREQUENCY_SECONDS,
140 label = "Configure poll frequency for port status and statistics; " +
Thomas Vachuskadb29dcf2017-03-31 11:26:19 -0700141 "default is 30 sec")
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700142 private int pollFrequency = DEFAULT_POLL_FREQUENCY_SECONDS;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530143
Michal Mach26a90fa2017-06-07 11:12:46 +0200144 protected ExecutorService executor =
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700145 Executors.newFixedThreadPool(5, groupedThreads("onos/netconfdeviceprovider",
146 "device-installer-%d", log));
Yuta HIGUCHI1624df12016-07-21 16:54:33 -0700147 protected ScheduledExecutorService connectionExecutor
148 = newScheduledThreadPool(CORE_POOL_SIZE,
149 groupedThreads("onos/netconfdeviceprovider",
150 "connection-executor-%d", log));
Andrea Campanella5c999e22016-03-01 15:12:53 -0800151
Michele Santuari576f09c2016-09-28 14:20:00 +0200152 protected DeviceProviderService providerService;
andreaeb70a942015-10-16 21:34:46 -0700153 private NetconfDeviceListener innerNodeListener = new InnerNetconfDeviceListener();
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700154 private InternalDeviceListener deviceListener = new InternalDeviceListener();
Michele Santuari576f09c2016-09-28 14:20:00 +0200155 protected ScheduledFuture<?> scheduledTask;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530156
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200157 protected final List<ConfigFactory> factories = ImmutableList.of(
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700158 // TODO consider moving Config registration to NETCONF ctl bundle
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200159 new ConfigFactory<DeviceId, NetconfDeviceConfig>(
160 SubjectFactories.DEVICE_SUBJECT_FACTORY,
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700161 NetconfDeviceConfig.class, NetconfDeviceConfig.CONFIG_KEY) {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200162 @Override
163 public NetconfDeviceConfig createConfig() {
164 return new NetconfDeviceConfig();
165 }
166 },
andreaeb70a942015-10-16 21:34:46 -0700167 new ConfigFactory<ApplicationId, NetconfProviderConfig>(APP_SUBJECT_FACTORY,
168 NetconfProviderConfig.class,
169 "devices",
170 true) {
171 @Override
172 public NetconfProviderConfig createConfig() {
173 return new NetconfProviderConfig();
174 }
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200175 });
176
Michele Santuari576f09c2016-09-28 14:20:00 +0200177 protected final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
andreaeb70a942015-10-16 21:34:46 -0700178 private ApplicationId appId;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700179 private boolean active;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530180
Sanjay Se8dcfee2015-04-23 10:07:08 +0530181
182 @Activate
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700183 public void activate(ComponentContext context) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700184 active = true;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700185 componentConfigService.registerProperties(getClass());
Sanjay Se8dcfee2015-04-23 10:07:08 +0530186 providerService = providerRegistry.register(this);
Andrea Campanella101417d2015-12-11 17:58:07 -0800187 appId = coreService.registerApplication(APP_NAME);
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200188 factories.forEach(cfgService::registerConfigFactory);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700189 cfgService.addListener(cfgListener);
andreaeb70a942015-10-16 21:34:46 -0700190 controller.addDeviceListener(innerNodeListener);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700191 deviceService.addListener(deviceListener);
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200192 translateConfig();
Andrea Campanella7d8449b2016-03-02 10:16:42 -0800193 executor.execute(NetconfDeviceProvider.this::connectDevices);
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700194 modified(context);
Thomas Vachuskad6811712015-04-29 21:37:04 -0700195 log.info("Started");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530196 }
197
andreaeb70a942015-10-16 21:34:46 -0700198
Sanjay Se8dcfee2015-04-23 10:07:08 +0530199 @Deactivate
andreaeb70a942015-10-16 21:34:46 -0700200 public void deactivate() {
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700201 componentConfigService.unregisterProperties(getClass(), false);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700202 deviceService.removeListener(deviceListener);
203 active = false;
204 controller.getNetconfDevices().forEach(id -> {
205 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(id.toString()));
206 controller.disconnectDevice(id, true);
207 });
Andrea Campanella86294db2016-03-07 11:42:49 -0800208 controller.removeDeviceListener(innerNodeListener);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700209 deviceService.removeListener(deviceListener);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530210 providerRegistry.unregister(this);
211 providerService = null;
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200212 factories.forEach(cfgService::unregisterConfigFactory);
helenyrwufd296b62016-06-22 17:43:02 -0700213 scheduledTask.cancel(true);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700214 executor.shutdown();
Sanjay Seb5eebb2015-04-24 15:44:50 +0530215 log.info("Stopped");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530216 }
217
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700218
219 @Modified
220 public void modified(ComponentContext context) {
221 if (context != null) {
222 Dictionary<?, ?> properties = context.getProperties();
223 pollFrequency = Tools.getIntegerProperty(properties, "pollFrequency",
224 DEFAULT_POLL_FREQUENCY_SECONDS);
225 log.info("Configured. Poll frequency is configured to {} seconds", pollFrequency);
226 }
227 if (scheduledTask != null) {
228 scheduledTask.cancel(false);
229 }
230 scheduledTask = schedulePolling();
231 }
232
andreaeb70a942015-10-16 21:34:46 -0700233 public NetconfDeviceProvider() {
Andrea Campanella101417d2015-12-11 17:58:07 -0800234 super(new ProviderId(SCHEME_NAME, DEVICE_PROVIDER_PACKAGE));
Sanjay Se8dcfee2015-04-23 10:07:08 +0530235 }
236
helenyrwufd296b62016-06-22 17:43:02 -0700237 // Checks connection to devices in the config file
238 // every DEFAULT_POLL_FREQUENCY_SECONDS seconds.
239 private ScheduledFuture schedulePolling() {
Yuta HIGUCHIc2b82e32017-03-10 14:33:41 -0800240 return connectionExecutor.scheduleAtFixedRate(exceptionSafe(this::checkAndUpdateDevices),
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700241 pollFrequency / 10,
242 pollFrequency, TimeUnit.SECONDS);
helenyrwufd296b62016-06-22 17:43:02 -0700243 }
244
Yuta HIGUCHIc2b82e32017-03-10 14:33:41 -0800245 private Runnable exceptionSafe(Runnable runnable) {
246 return new Runnable() {
247
248 @Override
249 public void run() {
250 try {
251 runnable.run();
252 } catch (Exception e) {
253 log.error("Unhandled Exception", e);
254 }
255 }
256 };
257 }
258
Sanjay Se8dcfee2015-04-23 10:07:08 +0530259 @Override
260 public void triggerProbe(DeviceId deviceId) {
andreaeb70a942015-10-16 21:34:46 -0700261 // TODO: This will be implemented later.
Yuta HIGUCHIe4cb8cf2017-05-01 22:18:28 -0700262 log.debug("Should be triggering probe on device {}", deviceId);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530263 }
264
265 @Override
266 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700267 if (active) {
268 switch (newRole) {
269 case MASTER:
270 initiateConnection(deviceId, newRole);
271 log.debug("Accepting mastership role change to {} for device {}", newRole, deviceId);
272 break;
273 case STANDBY:
274 controller.disconnectDevice(deviceId, false);
275 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.STANDBY);
276 //else no-op
277 break;
278 case NONE:
279 controller.disconnectDevice(deviceId, false);
280 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
281 break;
282 default:
283 log.error("Unimplemented Mastership state : {}", newRole);
284
285 }
286 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530287 }
288
289 @Override
290 public boolean isReachable(DeviceId deviceId) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700291 //FIXME this is a workaround util device state is shared
292 // between controller instances.
293 Device device = deviceService.getDevice(deviceId);
294 String ip;
295 int port;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700296 if (device != null) {
297 ip = device.annotations().value(IPADDRESS);
298 port = Integer.parseInt(device.annotations().value(PORT));
299 } else {
300 String[] info = deviceId.toString().split(":");
301 if (info.length == 3) {
302 ip = info[1];
303 port = Integer.parseInt(info[2]);
304 } else {
305 ip = Arrays.asList(info).stream().filter(el -> !el.equals(info[0])
306 && !el.equals(info[info.length - 1]))
307 .reduce((t, u) -> t + ":" + u)
308 .get();
309 log.debug("ip v6 {}", ip);
310 port = Integer.parseInt(info[info.length - 1]);
311 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530312 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700313 //test connection to device opening a socket to it.
Yuta HIGUCHI70c21472017-04-20 20:40:46 -0700314 try (Socket socket = new Socket(ip, port)) {
Yuta HIGUCHI0454d702017-03-17 10:08:38 -0700315 log.debug("rechability of {}, {}, {}", deviceId, socket.isConnected(), !socket.isClosed());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700316 return socket.isConnected() && !socket.isClosed();
317 } catch (IOException e) {
318 log.info("Device {} is not reachable", deviceId);
319 return false;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700320 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530321 }
322
Saurav Dasa2d37502016-03-25 17:50:40 -0700323 @Override
324 public void changePortState(DeviceId deviceId, PortNumber portNumber,
325 boolean enable) {
326 // TODO if required
327 }
328
andreaeb70a942015-10-16 21:34:46 -0700329 private class InnerNetconfDeviceListener implements NetconfDeviceListener {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530330
Andrea Campanella101417d2015-12-11 17:58:07 -0800331
andreaeb70a942015-10-16 21:34:46 -0700332 @Override
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700333 public void deviceAdded(DeviceId deviceId) {
334 //no-op
335 log.debug("Netconf device {} added to Netconf subController", deviceId);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530336 }
337
338 @Override
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700339 public void deviceRemoved(DeviceId deviceId) {
340 Preconditions.checkNotNull(deviceId, ISNULL);
helenyrwufd296b62016-06-22 17:43:02 -0700341
342 if (deviceService.getDevice(deviceId) != null) {
343 providerService.deviceDisconnected(deviceId);
344 log.debug("Netconf device {} removed from Netconf subController", deviceId);
345 } else {
346 log.warn("Netconf device {} does not exist in the store, " +
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530347 "it may already have been removed", deviceId);
helenyrwufd296b62016-06-22 17:43:02 -0700348 }
andreaeb70a942015-10-16 21:34:46 -0700349 }
350 }
351
andreaeb70a942015-10-16 21:34:46 -0700352 private void connectDevices() {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200353 Set<DeviceId> deviceSubjects =
354 cfgService.getSubjects(DeviceId.class, NetconfDeviceConfig.class);
355 deviceSubjects.forEach(deviceId -> {
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700356 connectDevice(cfgService.getConfig(deviceId, NetconfDeviceConfig.class));
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200357 });
helenyrwufd296b62016-06-22 17:43:02 -0700358 }
359
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700360
361 private void connectDevice(NetconfDeviceConfig config) {
362 if (config == null) {
363 return;
364 }
365 DeviceId deviceId = config.subject();
366 if (!deviceId.uri().getScheme().equals(SCHEME_NAME)) {
367 // not under my scheme, skipping
368 log.trace("{} not my scheme, skipping", deviceId);
369 return;
370 }
371 DeviceDescription deviceDescription = createDeviceRepresentation(deviceId, config);
372 log.debug("Connecting NETCONF device {}, on {}:{} with username {}",
373 deviceId, config.ip(), config.port(), config.username());
374 storeDeviceKey(config.sshKey(), config.username(), config.password(), deviceId);
375 if (deviceService.getDevice(deviceId) == null) {
376 providerService.deviceConnected(deviceId, deviceDescription);
377 }
378 try {
379 checkAndUpdateDevice(deviceId, deviceDescription);
380 } catch (Exception e) {
381 log.error("Unhandled exception checking {}", deviceId, e);
382 }
383 }
384
helenyrwufd296b62016-06-22 17:43:02 -0700385 private void checkAndUpdateDevice(DeviceId deviceId, DeviceDescription deviceDescription) {
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530386 Device device = deviceService.getDevice(deviceId);
387 if (device == null) {
Andrea Campanella7bbe7b12017-05-03 16:03:38 -0700388 log.debug("Device {} has not been added to store, " +
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200389 "since it's not reachable", deviceId);
helenyrwufd296b62016-06-22 17:43:02 -0700390 } else {
391 boolean isReachable = isReachable(deviceId);
392 if (isReachable && !deviceService.isAvailable(deviceId)) {
Konstantinos Kanonakis4d67dd82016-08-05 12:18:52 -0500393 if (device.is(DeviceDescriptionDiscovery.class)) {
394 if (mastershipService.isLocalMaster(deviceId)) {
395 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
396 device.as(DeviceDescriptionDiscovery.class);
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200397 DeviceDescription updatedDeviceDescription =
398 deviceDescriptionDiscovery.discoverDeviceDetails();
Michele Santuari00cc1f72016-09-08 17:05:24 +0200399 if (updatedDeviceDescription != null &&
400 !descriptionEquals(device, updatedDeviceDescription)) {
401 providerService.deviceConnected(
402 deviceId, new DefaultDeviceDescription(
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200403 updatedDeviceDescription, true,
404 updatedDeviceDescription.annotations()));
Michele Santuari576f09c2016-09-28 14:20:00 +0200405 } else if (updatedDeviceDescription == null) {
406 providerService.deviceConnected(
407 deviceId, new DefaultDeviceDescription(
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200408 deviceDescription, true,
409 deviceDescription.annotations()));
Michele Santuari00cc1f72016-09-08 17:05:24 +0200410 }
411 //if ports are not discovered, retry the discovery
412 if (deviceService.getPorts(deviceId).isEmpty()) {
413 discoverPorts(deviceId);
414 }
Konstantinos Kanonakis4d67dd82016-08-05 12:18:52 -0500415 }
416 } else {
Michele Santuari576f09c2016-09-28 14:20:00 +0200417 log.warn("No DeviceDescriptionDiscovery behaviour for device {} " +
418 "using DefaultDeviceDescription", deviceId);
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530419 providerService.deviceConnected(
420 deviceId, new DefaultDeviceDescription(
Michele Santuari576f09c2016-09-28 14:20:00 +0200421 deviceDescription, true, deviceDescription.annotations()));
Konstantinos Kanonakis4d67dd82016-08-05 12:18:52 -0500422 }
helenyrwufd296b62016-06-22 17:43:02 -0700423 } else if (!isReachable && deviceService.isAvailable(deviceId)) {
424 providerService.deviceDisconnected(deviceId);
Vidyashree Rama229554a2017-04-14 15:24:45 +0530425 } else if (isReachable && deviceService.isAvailable(deviceId) &&
426 mastershipService.isLocalMaster(deviceId)) {
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530427 updatePortStatistics(device);
helenyrwufd296b62016-06-22 17:43:02 -0700428 }
429 }
430 }
431
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530432 private void updatePortStatistics(Device device) {
433 if (device.is(PortStatisticsDiscovery.class)) {
434 PortStatisticsDiscovery d = device.as(PortStatisticsDiscovery.class);
Andrea Campanellac3627842017-04-04 18:06:54 +0200435 Collection<PortStatistics> portStatistics = d.discoverPortStatistics();
436 if (portStatistics != null) {
437 providerService.updatePortStatistics(device.id(),
438 portStatistics);
439 }
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530440 } else {
Yuta HIGUCHIe4cb8cf2017-05-01 22:18:28 -0700441 log.debug("No port statistics getter behaviour for device {}",
442 device.id());
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530443 }
444 }
445
Michele Santuari00cc1f72016-09-08 17:05:24 +0200446 private boolean descriptionEquals(Device device, DeviceDescription updatedDeviceDescription) {
Yuta HIGUCHIf381fc72017-01-03 10:39:36 -0800447 return Objects.equal(device.id().uri(), updatedDeviceDescription.deviceUri())
Michele Santuari00cc1f72016-09-08 17:05:24 +0200448 && Objects.equal(device.type(), updatedDeviceDescription.type())
449 && Objects.equal(device.manufacturer(), updatedDeviceDescription.manufacturer())
450 && Objects.equal(device.hwVersion(), updatedDeviceDescription.hwVersion())
451 && Objects.equal(device.swVersion(), updatedDeviceDescription.swVersion())
452 && Objects.equal(device.serialNumber(), updatedDeviceDescription.serialNumber())
453 && Objects.equal(device.chassisId(), updatedDeviceDescription.chassisId())
454 && Objects.equal(device.annotations(), updatedDeviceDescription.annotations());
455 }
456
helenyrwufd296b62016-06-22 17:43:02 -0700457 private void checkAndUpdateDevices() {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200458 Set<DeviceId> deviceSubjects =
459 cfgService.getSubjects(DeviceId.class, NetconfDeviceConfig.class);
460 deviceSubjects.forEach(deviceId -> {
461 NetconfDeviceConfig config =
462 cfgService.getConfig(deviceId, NetconfDeviceConfig.class);
463 DeviceDescription deviceDescription = createDeviceRepresentation(deviceId, config);
464 storeDeviceKey(config.sshKey(), config.username(), config.password(), deviceId);
465 checkAndUpdateDevice(deviceId, deviceDescription);
466 });
andreaeb70a942015-10-16 21:34:46 -0700467 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530468
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200469 private DeviceDescription createDeviceRepresentation(DeviceId deviceId, NetconfDeviceConfig config) {
470 Preconditions.checkNotNull(deviceId, ISNULL);
471 //Netconf configuration object
472 ChassisId cid = new ChassisId();
473 String ipAddress = config.ip().toString();
474 SparseAnnotations annotations = DefaultAnnotations.builder()
475 .set(IPADDRESS, ipAddress)
476 .set(PORT, String.valueOf(config.port()))
477 .set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase())
478 .build();
479 return new DefaultDeviceDescription(
480 deviceId.uri(),
481 Device.Type.SWITCH,
482 UNKNOWN, UNKNOWN,
483 UNKNOWN, UNKNOWN,
484 cid, false,
485 annotations);
486 }
487
488 private void storeDeviceKey(String sshKey, String username, String password, DeviceId deviceId) {
489 if (sshKey.equals("")) {
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600490 deviceKeyAdminService.addKey(
491 DeviceKey.createDeviceKeyUsingUsernamePassword(
492 DeviceKeyId.deviceKeyId(deviceId.toString()),
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200493 null, username, password));
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600494 } else {
495 deviceKeyAdminService.addKey(
496 DeviceKey.createDeviceKeyUsingSshKey(
497 DeviceKeyId.deviceKeyId(deviceId.toString()),
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200498 null, username, password,
499 sshKey));
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600500 }
501 }
502
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700503 private void initiateConnection(DeviceId deviceId, MastershipRole newRole) {
504 try {
505 if (isReachable(deviceId)) {
506 controller.connectDevice(deviceId);
507 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.MASTER);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700508 }
509 } catch (Exception e) {
510 if (deviceService.getDevice(deviceId) != null) {
511 providerService.deviceDisconnected(deviceId);
512 }
513 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(deviceId.toString()));
514 throw new RuntimeException(new NetconfException(
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200515 "Can't connect to NETCONF device " + deviceId, e));
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700516
517 }
518 }
519
520 private void discoverPorts(DeviceId deviceId) {
521 Device device = deviceService.getDevice(deviceId);
Andrea Campanella6c71a052016-04-22 11:56:31 -0700522 //TODO remove when PortDiscovery is removed from master
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700523 if (device.is(PortDiscovery.class)) {
524 PortDiscovery portConfig = device.as(PortDiscovery.class);
525 providerService.updatePorts(deviceId,
526 portConfig.getPorts());
Andrea Campanella6c71a052016-04-22 11:56:31 -0700527 } else if (device.is(DeviceDescriptionDiscovery.class)) {
528 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
529 device.as(DeviceDescriptionDiscovery.class);
530 providerService.updatePorts(deviceId,
531 deviceDescriptionDiscovery.discoverPortDetails());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700532 } else {
533 log.warn("No portGetter behaviour for device {}", deviceId);
534 }
Gaurav Agrawaldab4d772017-03-29 15:15:13 +0530535
536 // Port statistics discovery
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530537 updatePortStatistics(device);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700538 }
539
540 /**
541 * Return the DeviceId about the device containing the URI.
542 *
Andrea Campanella6c71a052016-04-22 11:56:31 -0700543 * @param ip IP address
Ray Milkeyd4334db2016-04-05 17:39:44 -0700544 * @param port port number
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700545 * @return DeviceId
546 */
547 public DeviceId getDeviceId(String ip, int port) {
548 try {
549 return DeviceId.deviceId(new URI(NETCONF, ip + ":" + port, null));
550 } catch (URISyntaxException e) {
551 throw new IllegalArgumentException("Unable to build deviceID for device "
552 + ip + ":" + port, e);
553 }
554 }
555
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200556
557 protected void translateConfig() {
558 NetconfProviderConfig cfg = cfgService.getConfig(appId, NetconfProviderConfig.class);
559 if (cfg != null) {
560 try {
561 cfg.getDevicesAddresses().forEach(addr -> {
562 DeviceId deviceId = getDeviceId(addr.ip().toString(), addr.port());
563 log.info("Translating config for device {}", deviceId);
564 if (cfgService.getConfig(deviceId, NetconfDeviceConfig.class) == null) {
565 ObjectMapper mapper = new ObjectMapper();
566 ObjectNode device = mapper.createObjectNode();
567 device.put("ip", addr.ip().toString());
568 device.put("port", addr.port());
569 device.put("username", addr.name());
570 device.put("password", addr.password());
571 device.put("sshkey", addr.sshkey());
572 cfgService.applyConfig(deviceId, NetconfDeviceConfig.class, device);
573 } else {
574 // This is a corner case where new updated config is
575 // pushed with old /app tree after an initial with the
576 // new device/ tree. Since old method will be deprecated
577 // it's ok to ignore
578 log.warn("Config for device {} already exists, ignoring", deviceId);
579 }
580
581 });
582 } catch (ConfigException e) {
583 log.error("Cannot read config error " + e);
584 }
585 }
586 }
587
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700588 /**
589 * Listener for configuration events.
590 */
andreaeb70a942015-10-16 21:34:46 -0700591 private class InternalNetworkConfigListener implements NetworkConfigListener {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530592
andreaeb70a942015-10-16 21:34:46 -0700593
594 @Override
595 public void event(NetworkConfigEvent event) {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200596 if (event.configClass().equals(NetconfDeviceConfig.class)) {
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700597 executor.execute(() -> connectDevice((NetconfDeviceConfig) event.config().get()));
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200598 } else {
599 log.warn("Injecting device via this Json is deprecated, " +
600 "please put configuration under devices/ as shown in the wiki");
601 translateConfig();
602 }
603
Sanjay Se8dcfee2015-04-23 10:07:08 +0530604 }
605
andreaeb70a942015-10-16 21:34:46 -0700606 @Override
607 public boolean isRelevant(NetworkConfigEvent event) {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200608 return (event.configClass().equals(NetconfDeviceConfig.class) ||
609 event.configClass().equals(NetconfProviderConfig.class)) &&
andreaeb70a942015-10-16 21:34:46 -0700610 (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
611 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530612 }
613 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700614
615 /**
616 * Listener for core device events.
617 */
618 private class InternalDeviceListener implements DeviceListener {
619 @Override
620 public void event(DeviceEvent event) {
621 if ((event.type() == DeviceEvent.Type.DEVICE_ADDED)) {
622 executor.execute(() -> discoverPorts(event.subject().id()));
623 } else if ((event.type() == DeviceEvent.Type.DEVICE_REMOVED)) {
624 log.debug("removing device {}", event.subject().id());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700625 controller.disconnectDevice(event.subject().id(), true);
626 }
627 }
628
629 @Override
630 public boolean isRelevant(DeviceEvent event) {
631 if (mastershipService.getMasterFor(event.subject().id()) == null) {
632 return true;
633 }
Andrea Campanella968f93f2017-06-08 11:09:28 +0200634 return (SCHEME_NAME.equalsIgnoreCase(event.subject().annotations().value(AnnotationKeys.PROTOCOL)) ||
635 (SCHEME_NAME.equalsIgnoreCase(event.subject().id().uri().getScheme()))) &&
Michele Santuari576f09c2016-09-28 14:20:00 +0200636 mastershipService.isLocalMaster(event.subject().id());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700637 }
638 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530639}