blob: 960b2698c7b1ff7fa609a6ad81b1fff121eb941c [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;
mskala832d0472017-06-09 16:31:42 +020082import java.util.Map;
Andrea Campanella34cf65c2017-04-12 13:51:32 +020083import java.util.Set;
mskala832d0472017-06-09 16:31:42 +020084import java.util.concurrent.ConcurrentHashMap;
Andrea Campanella5c999e22016-03-01 15:12:53 -080085import java.util.concurrent.ExecutorService;
86import java.util.concurrent.Executors;
helenyrwufd296b62016-06-22 17:43:02 -070087import java.util.concurrent.ScheduledExecutorService;
88import java.util.concurrent.ScheduledFuture;
89import java.util.concurrent.TimeUnit;
mskala832d0472017-06-09 16:31:42 +020090import java.util.concurrent.atomic.AtomicInteger;
andreaeb70a942015-10-16 21:34:46 -070091
Yuta HIGUCHI1624df12016-07-21 16:54:33 -070092import static java.util.concurrent.Executors.newScheduledThreadPool;
Andrea Campanella5c999e22016-03-01 15:12:53 -080093import static org.onlab.util.Tools.groupedThreads;
andreaeb70a942015-10-16 21:34:46 -070094import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
95import static org.slf4j.LoggerFactory.getLogger;
96
Sanjay Se8dcfee2015-04-23 10:07:08 +053097/**
andreaeb70a942015-10-16 21:34:46 -070098 * Provider which uses an NETCONF controller to detect device.
Sanjay Se8dcfee2015-04-23 10:07:08 +053099 */
100@Component(immediate = true)
101public class NetconfDeviceProvider extends AbstractProvider
102 implements DeviceProvider {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700103
andreaeb70a942015-10-16 21:34:46 -0700104 private final Logger log = getLogger(getClass());
Sanjay Se8dcfee2015-04-23 10:07:08 +0530105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
107 protected DeviceProviderRegistry providerRegistry;
108
andreaeb70a942015-10-16 21:34:46 -0700109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella101417d2015-12-11 17:58:07 -0800110 protected NetconfController controller;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
andreaeb70a942015-10-16 21:34:46 -0700113 protected NetworkConfigRegistry cfgService;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530114
Thomas Vachuskad6811712015-04-29 21:37:04 -0700115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
andreaeb70a942015-10-16 21:34:46 -0700116 protected CoreService coreService;
Thomas Vachuskad6811712015-04-29 21:37:04 -0700117
Aaron Kruglikov17b4c852016-01-15 16:37:04 -0800118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700119 protected DeviceService deviceService;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -0800120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700122 protected DeviceKeyAdminService deviceKeyAdminService;
123
124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
125 protected MastershipService mastershipService;
126
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700127 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
128 protected ComponentConfigService componentConfigService;
129
130
Michele Santuari576f09c2016-09-28 14:20:00 +0200131 protected static final String APP_NAME = "org.onosproject.netconf";
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200132 protected static final String SCHEME_NAME = "netconf";
Andrea Campanella101417d2015-12-11 17:58:07 -0800133 private static final String DEVICE_PROVIDER_PACKAGE = "org.onosproject.netconf.provider.device";
134 private static final String UNKNOWN = "unknown";
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700135 protected static final String ISNULL = "NetconfDeviceInfo is null";
136 private static final String IPADDRESS = "ipaddress";
137 private static final String NETCONF = "netconf";
138 private static final String PORT = "port";
helenyrwufd296b62016-06-22 17:43:02 -0700139 private static final int CORE_POOL_SIZE = 10;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700140
Thomas Vachuskadb29dcf2017-03-31 11:26:19 -0700141 private static final int DEFAULT_POLL_FREQUENCY_SECONDS = 30;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700142 @Property(name = "pollFrequency", intValue = DEFAULT_POLL_FREQUENCY_SECONDS,
143 label = "Configure poll frequency for port status and statistics; " +
Thomas Vachuskadb29dcf2017-03-31 11:26:19 -0700144 "default is 30 sec")
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700145 private int pollFrequency = DEFAULT_POLL_FREQUENCY_SECONDS;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530146
mskala832d0472017-06-09 16:31:42 +0200147 private static final int DEFAULT_MAX_RETRIES = 5;
148 @Property(name = "maxRetries", intValue = DEFAULT_MAX_RETRIES,
149 label = "Configure maximum allowed number of retries for obtaining list of ports; " +
150 "default is 5 times")
151 private int maxRetries = DEFAULT_MAX_RETRIES;
152
Michal Mach26a90fa2017-06-07 11:12:46 +0200153 protected ExecutorService executor =
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700154 Executors.newFixedThreadPool(5, groupedThreads("onos/netconfdeviceprovider",
155 "device-installer-%d", log));
Yuta HIGUCHI1624df12016-07-21 16:54:33 -0700156 protected ScheduledExecutorService connectionExecutor
157 = newScheduledThreadPool(CORE_POOL_SIZE,
158 groupedThreads("onos/netconfdeviceprovider",
159 "connection-executor-%d", log));
Andrea Campanella5c999e22016-03-01 15:12:53 -0800160
Michele Santuari576f09c2016-09-28 14:20:00 +0200161 protected DeviceProviderService providerService;
andreaeb70a942015-10-16 21:34:46 -0700162 private NetconfDeviceListener innerNodeListener = new InnerNetconfDeviceListener();
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700163 private InternalDeviceListener deviceListener = new InternalDeviceListener();
mskala832d0472017-06-09 16:31:42 +0200164 private final Map<DeviceId, AtomicInteger> retriedPortDiscoveryMap = new ConcurrentHashMap<>();
Michele Santuari576f09c2016-09-28 14:20:00 +0200165 protected ScheduledFuture<?> scheduledTask;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530166
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200167 protected final List<ConfigFactory> factories = ImmutableList.of(
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700168 // TODO consider moving Config registration to NETCONF ctl bundle
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200169 new ConfigFactory<DeviceId, NetconfDeviceConfig>(
170 SubjectFactories.DEVICE_SUBJECT_FACTORY,
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700171 NetconfDeviceConfig.class, NetconfDeviceConfig.CONFIG_KEY) {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200172 @Override
173 public NetconfDeviceConfig createConfig() {
174 return new NetconfDeviceConfig();
175 }
176 },
andreaeb70a942015-10-16 21:34:46 -0700177 new ConfigFactory<ApplicationId, NetconfProviderConfig>(APP_SUBJECT_FACTORY,
178 NetconfProviderConfig.class,
Palash Kalac3ffad92017-06-09 21:18:19 +0900179 "netconf_devices",
andreaeb70a942015-10-16 21:34:46 -0700180 true) {
181 @Override
182 public NetconfProviderConfig createConfig() {
183 return new NetconfProviderConfig();
184 }
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200185 });
186
Michele Santuari576f09c2016-09-28 14:20:00 +0200187 protected final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
andreaeb70a942015-10-16 21:34:46 -0700188 private ApplicationId appId;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700189 private boolean active;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530190
Sanjay Se8dcfee2015-04-23 10:07:08 +0530191
192 @Activate
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700193 public void activate(ComponentContext context) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700194 active = true;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700195 componentConfigService.registerProperties(getClass());
Sanjay Se8dcfee2015-04-23 10:07:08 +0530196 providerService = providerRegistry.register(this);
Andrea Campanella101417d2015-12-11 17:58:07 -0800197 appId = coreService.registerApplication(APP_NAME);
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200198 factories.forEach(cfgService::registerConfigFactory);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700199 cfgService.addListener(cfgListener);
andreaeb70a942015-10-16 21:34:46 -0700200 controller.addDeviceListener(innerNodeListener);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700201 deviceService.addListener(deviceListener);
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200202 translateConfig();
Andrea Campanella7d8449b2016-03-02 10:16:42 -0800203 executor.execute(NetconfDeviceProvider.this::connectDevices);
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700204 modified(context);
Thomas Vachuskad6811712015-04-29 21:37:04 -0700205 log.info("Started");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530206 }
207
andreaeb70a942015-10-16 21:34:46 -0700208
Sanjay Se8dcfee2015-04-23 10:07:08 +0530209 @Deactivate
andreaeb70a942015-10-16 21:34:46 -0700210 public void deactivate() {
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700211 componentConfigService.unregisterProperties(getClass(), false);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700212 deviceService.removeListener(deviceListener);
213 active = false;
214 controller.getNetconfDevices().forEach(id -> {
215 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(id.toString()));
216 controller.disconnectDevice(id, true);
217 });
Andrea Campanella86294db2016-03-07 11:42:49 -0800218 controller.removeDeviceListener(innerNodeListener);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700219 deviceService.removeListener(deviceListener);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530220 providerRegistry.unregister(this);
221 providerService = null;
mskala832d0472017-06-09 16:31:42 +0200222 retriedPortDiscoveryMap.clear();
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200223 factories.forEach(cfgService::unregisterConfigFactory);
helenyrwufd296b62016-06-22 17:43:02 -0700224 scheduledTask.cancel(true);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700225 executor.shutdown();
Sanjay Seb5eebb2015-04-24 15:44:50 +0530226 log.info("Stopped");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530227 }
228
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700229
230 @Modified
231 public void modified(ComponentContext context) {
232 if (context != null) {
233 Dictionary<?, ?> properties = context.getProperties();
234 pollFrequency = Tools.getIntegerProperty(properties, "pollFrequency",
235 DEFAULT_POLL_FREQUENCY_SECONDS);
236 log.info("Configured. Poll frequency is configured to {} seconds", pollFrequency);
mskala832d0472017-06-09 16:31:42 +0200237
238 maxRetries = Tools.getIntegerProperty(properties, "maxRetries",
239 DEFAULT_MAX_RETRIES);
240 log.info("Configured. Number of retries is configured to {} times", maxRetries);
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700241 }
242 if (scheduledTask != null) {
243 scheduledTask.cancel(false);
244 }
245 scheduledTask = schedulePolling();
246 }
247
andreaeb70a942015-10-16 21:34:46 -0700248 public NetconfDeviceProvider() {
Andrea Campanella101417d2015-12-11 17:58:07 -0800249 super(new ProviderId(SCHEME_NAME, DEVICE_PROVIDER_PACKAGE));
Sanjay Se8dcfee2015-04-23 10:07:08 +0530250 }
251
helenyrwufd296b62016-06-22 17:43:02 -0700252 // Checks connection to devices in the config file
253 // every DEFAULT_POLL_FREQUENCY_SECONDS seconds.
254 private ScheduledFuture schedulePolling() {
Yuta HIGUCHIc2b82e32017-03-10 14:33:41 -0800255 return connectionExecutor.scheduleAtFixedRate(exceptionSafe(this::checkAndUpdateDevices),
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700256 pollFrequency / 10,
257 pollFrequency, TimeUnit.SECONDS);
helenyrwufd296b62016-06-22 17:43:02 -0700258 }
259
Yuta HIGUCHIc2b82e32017-03-10 14:33:41 -0800260 private Runnable exceptionSafe(Runnable runnable) {
261 return new Runnable() {
262
263 @Override
264 public void run() {
265 try {
266 runnable.run();
267 } catch (Exception e) {
268 log.error("Unhandled Exception", e);
269 }
270 }
271 };
272 }
273
Sanjay Se8dcfee2015-04-23 10:07:08 +0530274 @Override
275 public void triggerProbe(DeviceId deviceId) {
andreaeb70a942015-10-16 21:34:46 -0700276 // TODO: This will be implemented later.
Yuta HIGUCHIe4cb8cf2017-05-01 22:18:28 -0700277 log.debug("Should be triggering probe on device {}", deviceId);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530278 }
279
280 @Override
281 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700282 if (active) {
283 switch (newRole) {
284 case MASTER:
285 initiateConnection(deviceId, newRole);
286 log.debug("Accepting mastership role change to {} for device {}", newRole, deviceId);
287 break;
288 case STANDBY:
289 controller.disconnectDevice(deviceId, false);
290 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.STANDBY);
291 //else no-op
292 break;
293 case NONE:
294 controller.disconnectDevice(deviceId, false);
295 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
296 break;
297 default:
298 log.error("Unimplemented Mastership state : {}", newRole);
299
300 }
301 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530302 }
303
304 @Override
305 public boolean isReachable(DeviceId deviceId) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700306 //FIXME this is a workaround util device state is shared
307 // between controller instances.
308 Device device = deviceService.getDevice(deviceId);
309 String ip;
310 int port;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700311 if (device != null) {
312 ip = device.annotations().value(IPADDRESS);
313 port = Integer.parseInt(device.annotations().value(PORT));
314 } else {
315 String[] info = deviceId.toString().split(":");
316 if (info.length == 3) {
317 ip = info[1];
318 port = Integer.parseInt(info[2]);
319 } else {
320 ip = Arrays.asList(info).stream().filter(el -> !el.equals(info[0])
321 && !el.equals(info[info.length - 1]))
322 .reduce((t, u) -> t + ":" + u)
323 .get();
324 log.debug("ip v6 {}", ip);
325 port = Integer.parseInt(info[info.length - 1]);
326 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530327 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700328 //test connection to device opening a socket to it.
Yuta HIGUCHI70c21472017-04-20 20:40:46 -0700329 try (Socket socket = new Socket(ip, port)) {
Yuta HIGUCHI0454d702017-03-17 10:08:38 -0700330 log.debug("rechability of {}, {}, {}", deviceId, socket.isConnected(), !socket.isClosed());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700331 return socket.isConnected() && !socket.isClosed();
332 } catch (IOException e) {
333 log.info("Device {} is not reachable", deviceId);
334 return false;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700335 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530336 }
337
Saurav Dasa2d37502016-03-25 17:50:40 -0700338 @Override
339 public void changePortState(DeviceId deviceId, PortNumber portNumber,
340 boolean enable) {
341 // TODO if required
342 }
343
andreaeb70a942015-10-16 21:34:46 -0700344 private class InnerNetconfDeviceListener implements NetconfDeviceListener {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530345
Andrea Campanella101417d2015-12-11 17:58:07 -0800346
andreaeb70a942015-10-16 21:34:46 -0700347 @Override
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700348 public void deviceAdded(DeviceId deviceId) {
349 //no-op
350 log.debug("Netconf device {} added to Netconf subController", deviceId);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530351 }
352
353 @Override
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700354 public void deviceRemoved(DeviceId deviceId) {
355 Preconditions.checkNotNull(deviceId, ISNULL);
helenyrwufd296b62016-06-22 17:43:02 -0700356
357 if (deviceService.getDevice(deviceId) != null) {
358 providerService.deviceDisconnected(deviceId);
mskala832d0472017-06-09 16:31:42 +0200359 retriedPortDiscoveryMap.remove(deviceId);
helenyrwufd296b62016-06-22 17:43:02 -0700360 log.debug("Netconf device {} removed from Netconf subController", deviceId);
361 } else {
362 log.warn("Netconf device {} does not exist in the store, " +
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530363 "it may already have been removed", deviceId);
helenyrwufd296b62016-06-22 17:43:02 -0700364 }
andreaeb70a942015-10-16 21:34:46 -0700365 }
366 }
367
andreaeb70a942015-10-16 21:34:46 -0700368 private void connectDevices() {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200369 Set<DeviceId> deviceSubjects =
370 cfgService.getSubjects(DeviceId.class, NetconfDeviceConfig.class);
371 deviceSubjects.forEach(deviceId -> {
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700372 connectDevice(cfgService.getConfig(deviceId, NetconfDeviceConfig.class));
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200373 });
helenyrwufd296b62016-06-22 17:43:02 -0700374 }
375
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700376
377 private void connectDevice(NetconfDeviceConfig config) {
378 if (config == null) {
379 return;
380 }
381 DeviceId deviceId = config.subject();
382 if (!deviceId.uri().getScheme().equals(SCHEME_NAME)) {
383 // not under my scheme, skipping
384 log.trace("{} not my scheme, skipping", deviceId);
385 return;
386 }
387 DeviceDescription deviceDescription = createDeviceRepresentation(deviceId, config);
388 log.debug("Connecting NETCONF device {}, on {}:{} with username {}",
389 deviceId, config.ip(), config.port(), config.username());
390 storeDeviceKey(config.sshKey(), config.username(), config.password(), deviceId);
mskala832d0472017-06-09 16:31:42 +0200391 retriedPortDiscoveryMap.putIfAbsent(deviceId, new AtomicInteger(0));
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700392 if (deviceService.getDevice(deviceId) == null) {
393 providerService.deviceConnected(deviceId, deviceDescription);
394 }
395 try {
396 checkAndUpdateDevice(deviceId, deviceDescription);
397 } catch (Exception e) {
398 log.error("Unhandled exception checking {}", deviceId, e);
399 }
400 }
401
helenyrwufd296b62016-06-22 17:43:02 -0700402 private void checkAndUpdateDevice(DeviceId deviceId, DeviceDescription deviceDescription) {
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530403 Device device = deviceService.getDevice(deviceId);
404 if (device == null) {
Andrea Campanella7bbe7b12017-05-03 16:03:38 -0700405 log.debug("Device {} has not been added to store, " +
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200406 "since it's not reachable", deviceId);
helenyrwufd296b62016-06-22 17:43:02 -0700407 } else {
408 boolean isReachable = isReachable(deviceId);
409 if (isReachable && !deviceService.isAvailable(deviceId)) {
Konstantinos Kanonakis4d67dd82016-08-05 12:18:52 -0500410 if (device.is(DeviceDescriptionDiscovery.class)) {
411 if (mastershipService.isLocalMaster(deviceId)) {
412 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
413 device.as(DeviceDescriptionDiscovery.class);
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200414 DeviceDescription updatedDeviceDescription =
415 deviceDescriptionDiscovery.discoverDeviceDetails();
Michele Santuari00cc1f72016-09-08 17:05:24 +0200416 if (updatedDeviceDescription != null &&
417 !descriptionEquals(device, updatedDeviceDescription)) {
418 providerService.deviceConnected(
419 deviceId, new DefaultDeviceDescription(
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200420 updatedDeviceDescription, true,
421 updatedDeviceDescription.annotations()));
Michele Santuari576f09c2016-09-28 14:20:00 +0200422 } else if (updatedDeviceDescription == null) {
423 providerService.deviceConnected(
424 deviceId, new DefaultDeviceDescription(
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200425 deviceDescription, true,
426 deviceDescription.annotations()));
Michele Santuari00cc1f72016-09-08 17:05:24 +0200427 }
Konstantinos Kanonakis4d67dd82016-08-05 12:18:52 -0500428 }
429 } else {
Michele Santuari576f09c2016-09-28 14:20:00 +0200430 log.warn("No DeviceDescriptionDiscovery behaviour for device {} " +
431 "using DefaultDeviceDescription", deviceId);
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530432 providerService.deviceConnected(
433 deviceId, new DefaultDeviceDescription(
Michele Santuari576f09c2016-09-28 14:20:00 +0200434 deviceDescription, true, deviceDescription.annotations()));
Konstantinos Kanonakis4d67dd82016-08-05 12:18:52 -0500435 }
helenyrwufd296b62016-06-22 17:43:02 -0700436 } else if (!isReachable && deviceService.isAvailable(deviceId)) {
437 providerService.deviceDisconnected(deviceId);
Vidyashree Rama229554a2017-04-14 15:24:45 +0530438 } else if (isReachable && deviceService.isAvailable(deviceId) &&
439 mastershipService.isLocalMaster(deviceId)) {
mskala832d0472017-06-09 16:31:42 +0200440
441 //if ports are not discovered, retry the discovery
442 if (deviceService.getPorts(deviceId).isEmpty() &&
443 retriedPortDiscoveryMap.get(deviceId).getAndIncrement() < maxRetries) {
444 discoverPorts(deviceId);
445 }
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530446 updatePortStatistics(device);
helenyrwufd296b62016-06-22 17:43:02 -0700447 }
448 }
449 }
450
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530451 private void updatePortStatistics(Device device) {
452 if (device.is(PortStatisticsDiscovery.class)) {
453 PortStatisticsDiscovery d = device.as(PortStatisticsDiscovery.class);
Andrea Campanellac3627842017-04-04 18:06:54 +0200454 Collection<PortStatistics> portStatistics = d.discoverPortStatistics();
455 if (portStatistics != null) {
456 providerService.updatePortStatistics(device.id(),
457 portStatistics);
458 }
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530459 } else {
Yuta HIGUCHIe4cb8cf2017-05-01 22:18:28 -0700460 log.debug("No port statistics getter behaviour for device {}",
461 device.id());
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530462 }
463 }
464
Michele Santuari00cc1f72016-09-08 17:05:24 +0200465 private boolean descriptionEquals(Device device, DeviceDescription updatedDeviceDescription) {
Yuta HIGUCHIf381fc72017-01-03 10:39:36 -0800466 return Objects.equal(device.id().uri(), updatedDeviceDescription.deviceUri())
Michele Santuari00cc1f72016-09-08 17:05:24 +0200467 && Objects.equal(device.type(), updatedDeviceDescription.type())
468 && Objects.equal(device.manufacturer(), updatedDeviceDescription.manufacturer())
469 && Objects.equal(device.hwVersion(), updatedDeviceDescription.hwVersion())
470 && Objects.equal(device.swVersion(), updatedDeviceDescription.swVersion())
471 && Objects.equal(device.serialNumber(), updatedDeviceDescription.serialNumber())
472 && Objects.equal(device.chassisId(), updatedDeviceDescription.chassisId())
473 && Objects.equal(device.annotations(), updatedDeviceDescription.annotations());
474 }
475
helenyrwufd296b62016-06-22 17:43:02 -0700476 private void checkAndUpdateDevices() {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200477 Set<DeviceId> deviceSubjects =
478 cfgService.getSubjects(DeviceId.class, NetconfDeviceConfig.class);
479 deviceSubjects.forEach(deviceId -> {
480 NetconfDeviceConfig config =
481 cfgService.getConfig(deviceId, NetconfDeviceConfig.class);
482 DeviceDescription deviceDescription = createDeviceRepresentation(deviceId, config);
483 storeDeviceKey(config.sshKey(), config.username(), config.password(), deviceId);
484 checkAndUpdateDevice(deviceId, deviceDescription);
485 });
andreaeb70a942015-10-16 21:34:46 -0700486 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530487
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200488 private DeviceDescription createDeviceRepresentation(DeviceId deviceId, NetconfDeviceConfig config) {
489 Preconditions.checkNotNull(deviceId, ISNULL);
490 //Netconf configuration object
491 ChassisId cid = new ChassisId();
492 String ipAddress = config.ip().toString();
493 SparseAnnotations annotations = DefaultAnnotations.builder()
494 .set(IPADDRESS, ipAddress)
495 .set(PORT, String.valueOf(config.port()))
496 .set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase())
497 .build();
498 return new DefaultDeviceDescription(
499 deviceId.uri(),
500 Device.Type.SWITCH,
501 UNKNOWN, UNKNOWN,
502 UNKNOWN, UNKNOWN,
503 cid, false,
504 annotations);
505 }
506
507 private void storeDeviceKey(String sshKey, String username, String password, DeviceId deviceId) {
508 if (sshKey.equals("")) {
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600509 deviceKeyAdminService.addKey(
510 DeviceKey.createDeviceKeyUsingUsernamePassword(
511 DeviceKeyId.deviceKeyId(deviceId.toString()),
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200512 null, username, password));
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600513 } else {
514 deviceKeyAdminService.addKey(
515 DeviceKey.createDeviceKeyUsingSshKey(
516 DeviceKeyId.deviceKeyId(deviceId.toString()),
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200517 null, username, password,
518 sshKey));
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600519 }
520 }
521
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700522 private void initiateConnection(DeviceId deviceId, MastershipRole newRole) {
523 try {
524 if (isReachable(deviceId)) {
525 controller.connectDevice(deviceId);
526 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.MASTER);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700527 }
528 } catch (Exception e) {
529 if (deviceService.getDevice(deviceId) != null) {
530 providerService.deviceDisconnected(deviceId);
531 }
532 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(deviceId.toString()));
533 throw new RuntimeException(new NetconfException(
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200534 "Can't connect to NETCONF device " + deviceId, e));
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700535
536 }
537 }
538
539 private void discoverPorts(DeviceId deviceId) {
540 Device device = deviceService.getDevice(deviceId);
Andrea Campanella6c71a052016-04-22 11:56:31 -0700541 //TODO remove when PortDiscovery is removed from master
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700542 if (device.is(PortDiscovery.class)) {
543 PortDiscovery portConfig = device.as(PortDiscovery.class);
544 providerService.updatePorts(deviceId,
545 portConfig.getPorts());
Andrea Campanella6c71a052016-04-22 11:56:31 -0700546 } else if (device.is(DeviceDescriptionDiscovery.class)) {
547 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
548 device.as(DeviceDescriptionDiscovery.class);
549 providerService.updatePorts(deviceId,
550 deviceDescriptionDiscovery.discoverPortDetails());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700551 } else {
552 log.warn("No portGetter behaviour for device {}", deviceId);
553 }
Gaurav Agrawaldab4d772017-03-29 15:15:13 +0530554
555 // Port statistics discovery
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530556 updatePortStatistics(device);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700557 }
558
559 /**
560 * Return the DeviceId about the device containing the URI.
561 *
Andrea Campanella6c71a052016-04-22 11:56:31 -0700562 * @param ip IP address
Ray Milkeyd4334db2016-04-05 17:39:44 -0700563 * @param port port number
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700564 * @return DeviceId
565 */
566 public DeviceId getDeviceId(String ip, int port) {
567 try {
568 return DeviceId.deviceId(new URI(NETCONF, ip + ":" + port, null));
569 } catch (URISyntaxException e) {
570 throw new IllegalArgumentException("Unable to build deviceID for device "
571 + ip + ":" + port, e);
572 }
573 }
574
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200575
576 protected void translateConfig() {
577 NetconfProviderConfig cfg = cfgService.getConfig(appId, NetconfProviderConfig.class);
578 if (cfg != null) {
579 try {
580 cfg.getDevicesAddresses().forEach(addr -> {
581 DeviceId deviceId = getDeviceId(addr.ip().toString(), addr.port());
582 log.info("Translating config for device {}", deviceId);
583 if (cfgService.getConfig(deviceId, NetconfDeviceConfig.class) == null) {
584 ObjectMapper mapper = new ObjectMapper();
585 ObjectNode device = mapper.createObjectNode();
586 device.put("ip", addr.ip().toString());
587 device.put("port", addr.port());
588 device.put("username", addr.name());
589 device.put("password", addr.password());
590 device.put("sshkey", addr.sshkey());
591 cfgService.applyConfig(deviceId, NetconfDeviceConfig.class, device);
592 } else {
593 // This is a corner case where new updated config is
594 // pushed with old /app tree after an initial with the
595 // new device/ tree. Since old method will be deprecated
596 // it's ok to ignore
597 log.warn("Config for device {} already exists, ignoring", deviceId);
598 }
599
600 });
601 } catch (ConfigException e) {
602 log.error("Cannot read config error " + e);
603 }
604 }
605 }
606
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700607 /**
608 * Listener for configuration events.
609 */
andreaeb70a942015-10-16 21:34:46 -0700610 private class InternalNetworkConfigListener implements NetworkConfigListener {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530611
andreaeb70a942015-10-16 21:34:46 -0700612
613 @Override
614 public void event(NetworkConfigEvent event) {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200615 if (event.configClass().equals(NetconfDeviceConfig.class)) {
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700616 executor.execute(() -> connectDevice((NetconfDeviceConfig) event.config().get()));
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200617 } else {
618 log.warn("Injecting device via this Json is deprecated, " +
619 "please put configuration under devices/ as shown in the wiki");
620 translateConfig();
621 }
622
Sanjay Se8dcfee2015-04-23 10:07:08 +0530623 }
624
andreaeb70a942015-10-16 21:34:46 -0700625 @Override
626 public boolean isRelevant(NetworkConfigEvent event) {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200627 return (event.configClass().equals(NetconfDeviceConfig.class) ||
628 event.configClass().equals(NetconfProviderConfig.class)) &&
andreaeb70a942015-10-16 21:34:46 -0700629 (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
630 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530631 }
632 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700633
634 /**
635 * Listener for core device events.
636 */
637 private class InternalDeviceListener implements DeviceListener {
638 @Override
639 public void event(DeviceEvent event) {
640 if ((event.type() == DeviceEvent.Type.DEVICE_ADDED)) {
641 executor.execute(() -> discoverPorts(event.subject().id()));
642 } else if ((event.type() == DeviceEvent.Type.DEVICE_REMOVED)) {
643 log.debug("removing device {}", event.subject().id());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700644 controller.disconnectDevice(event.subject().id(), true);
645 }
646 }
647
648 @Override
649 public boolean isRelevant(DeviceEvent event) {
650 if (mastershipService.getMasterFor(event.subject().id()) == null) {
651 return true;
652 }
Andrea Campanella968f93f2017-06-08 11:09:28 +0200653 return (SCHEME_NAME.equalsIgnoreCase(event.subject().annotations().value(AnnotationKeys.PROTOCOL)) ||
654 (SCHEME_NAME.equalsIgnoreCase(event.subject().id().uri().getScheme()))) &&
Michele Santuari576f09c2016-09-28 14:20:00 +0200655 mastershipService.isLocalMaster(event.subject().id());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700656 }
657 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530658}