blob: f675dbc8a1de28e29650d2e45ba43adc9d979428 [file] [log] [blame]
Sanjay Se8dcfee2015-04-23 10:07:08 +05301/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Sanjay Se8dcfee2015-04-23 10:07:08 +05303 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
andreaeb70a942015-10-16 21:34:46 -070016
Sanjay Se8dcfee2015-04-23 10:07:08 +053017package org.onosproject.provider.netconf.device.impl;
18
Michele Santuari00cc1f72016-09-08 17:05:24 +020019import com.google.common.base.Objects;
andreaeb70a942015-10-16 21:34:46 -070020import com.google.common.base.Preconditions;
Sanjay Se8dcfee2015-04-23 10:07:08 +053021import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -070024import org.apache.felix.scr.annotations.Modified;
25import org.apache.felix.scr.annotations.Property;
Sanjay Se8dcfee2015-04-23 10:07:08 +053026import org.apache.felix.scr.annotations.Reference;
27import org.apache.felix.scr.annotations.ReferenceCardinality;
28import org.onlab.packet.ChassisId;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -070029import org.onlab.util.Tools;
30import org.onosproject.cfg.ComponentConfigService;
andreaeb70a942015-10-16 21:34:46 -070031import org.onosproject.core.ApplicationId;
32import org.onosproject.core.CoreService;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070033import org.onosproject.mastership.MastershipService;
Marc De Leenheerb0d131c2016-03-01 20:34:59 -080034import org.onosproject.net.AnnotationKeys;
andreaeb70a942015-10-16 21:34:46 -070035import org.onosproject.net.DefaultAnnotations;
Sanjay Se8dcfee2015-04-23 10:07:08 +053036import org.onosproject.net.Device;
37import org.onosproject.net.DeviceId;
38import org.onosproject.net.MastershipRole;
Ray Milkey02710432018-02-13 17:08:28 -080039import org.onosproject.net.Port;
Saurav Dasa2d37502016-03-25 17:50:40 -070040import org.onosproject.net.PortNumber;
andreaeb70a942015-10-16 21:34:46 -070041import org.onosproject.net.SparseAnnotations;
Ray Milkey02710432018-02-13 17:08:28 -080042import org.onosproject.net.behaviour.PortAdmin;
andreaeb70a942015-10-16 21:34:46 -070043import org.onosproject.net.config.ConfigFactory;
44import org.onosproject.net.config.NetworkConfigEvent;
45import org.onosproject.net.config.NetworkConfigListener;
46import org.onosproject.net.config.NetworkConfigRegistry;
Andrea Campanella34cf65c2017-04-12 13:51:32 +020047import org.onosproject.net.config.basics.SubjectFactories;
Sanjay Se8dcfee2015-04-23 10:07:08 +053048import org.onosproject.net.device.DefaultDeviceDescription;
Andrea Campanella32813682017-10-23 15:29:24 +020049import org.onosproject.net.device.DefaultPortDescription;
Sanjay Se8dcfee2015-04-23 10:07:08 +053050import org.onosproject.net.device.DeviceDescription;
Andrea Campanella6c71a052016-04-22 11:56:31 -070051import org.onosproject.net.device.DeviceDescriptionDiscovery;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070052import org.onosproject.net.device.DeviceEvent;
53import org.onosproject.net.device.DeviceListener;
Sanjay Se8dcfee2015-04-23 10:07:08 +053054import org.onosproject.net.device.DeviceProvider;
55import org.onosproject.net.device.DeviceProviderRegistry;
56import org.onosproject.net.device.DeviceProviderService;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -080057import org.onosproject.net.device.DeviceService;
Andrea Campanellac3627842017-04-04 18:06:54 +020058import org.onosproject.net.device.PortStatistics;
Gaurav Agrawaldab4d772017-03-29 15:15:13 +053059import org.onosproject.net.device.PortStatisticsDiscovery;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070060import org.onosproject.net.key.DeviceKey;
61import org.onosproject.net.key.DeviceKeyAdminService;
62import org.onosproject.net.key.DeviceKeyId;
Sanjay Se8dcfee2015-04-23 10:07:08 +053063import org.onosproject.net.provider.AbstractProvider;
64import org.onosproject.net.provider.ProviderId;
andreaeb70a942015-10-16 21:34:46 -070065import org.onosproject.netconf.NetconfController;
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -070066import org.onosproject.netconf.NetconfDevice;
andreaeb70a942015-10-16 21:34:46 -070067import org.onosproject.netconf.NetconfDeviceListener;
Andrea Campanella8b1cb672016-01-25 13:58:58 -080068import org.onosproject.netconf.NetconfException;
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -070069import org.onosproject.netconf.config.NetconfDeviceConfig;
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;
mskala832d0472017-06-09 16:31:42 +020080import java.util.Map;
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -070081import java.util.Optional;
Andrea Campanella34cf65c2017-04-12 13:51:32 +020082import java.util.Set;
Andrea Campanella32813682017-10-23 15:29:24 +020083import java.util.concurrent.CompletableFuture;
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.slf4j.LoggerFactory.getLogger;
95
Sanjay Se8dcfee2015-04-23 10:07:08 +053096/**
andreaeb70a942015-10-16 21:34:46 -070097 * Provider which uses an NETCONF controller to detect device.
Sanjay Se8dcfee2015-04-23 10:07:08 +053098 */
99@Component(immediate = true)
100public class NetconfDeviceProvider extends AbstractProvider
101 implements DeviceProvider {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700102
andreaeb70a942015-10-16 21:34:46 -0700103 private final Logger log = getLogger(getClass());
Sanjay Se8dcfee2015-04-23 10:07:08 +0530104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected DeviceProviderRegistry providerRegistry;
107
andreaeb70a942015-10-16 21:34:46 -0700108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella101417d2015-12-11 17:58:07 -0800109 protected NetconfController controller;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
andreaeb70a942015-10-16 21:34:46 -0700112 protected NetworkConfigRegistry cfgService;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530113
Thomas Vachuskad6811712015-04-29 21:37:04 -0700114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
andreaeb70a942015-10-16 21:34:46 -0700115 protected CoreService coreService;
Thomas Vachuskad6811712015-04-29 21:37:04 -0700116
Aaron Kruglikov17b4c852016-01-15 16:37:04 -0800117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700118 protected DeviceService deviceService;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -0800119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700121 protected DeviceKeyAdminService deviceKeyAdminService;
122
123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
124 protected MastershipService mastershipService;
125
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
127 protected ComponentConfigService componentConfigService;
128
129
Michele Santuari576f09c2016-09-28 14:20:00 +0200130 protected static final String APP_NAME = "org.onosproject.netconf";
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200131 protected static final String SCHEME_NAME = "netconf";
Andrea Campanella101417d2015-12-11 17:58:07 -0800132 private static final String DEVICE_PROVIDER_PACKAGE = "org.onosproject.netconf.provider.device";
133 private static final String UNKNOWN = "unknown";
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700134 protected static final String ISNULL = "NetconfDeviceInfo is null";
135 private static final String IPADDRESS = "ipaddress";
136 private static final String NETCONF = "netconf";
137 private static final String PORT = "port";
helenyrwufd296b62016-06-22 17:43:02 -0700138 private static final int CORE_POOL_SIZE = 10;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700139
Thomas Vachuskadb29dcf2017-03-31 11:26:19 -0700140 private static final int DEFAULT_POLL_FREQUENCY_SECONDS = 30;
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700141 @Property(name = "pollFrequency", intValue = DEFAULT_POLL_FREQUENCY_SECONDS,
142 label = "Configure poll frequency for port status and statistics; " +
Thomas Vachuskadb29dcf2017-03-31 11:26:19 -0700143 "default is 30 sec")
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700144 private int pollFrequency = DEFAULT_POLL_FREQUENCY_SECONDS;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530145
mskala832d0472017-06-09 16:31:42 +0200146 private static final int DEFAULT_MAX_RETRIES = 5;
147 @Property(name = "maxRetries", intValue = DEFAULT_MAX_RETRIES,
148 label = "Configure maximum allowed number of retries for obtaining list of ports; " +
149 "default is 5 times")
150 private int maxRetries = DEFAULT_MAX_RETRIES;
151
Michal Mach26a90fa2017-06-07 11:12:46 +0200152 protected ExecutorService executor =
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700153 Executors.newFixedThreadPool(5, groupedThreads("onos/netconfdeviceprovider",
154 "device-installer-%d", log));
Yuta HIGUCHI1624df12016-07-21 16:54:33 -0700155 protected ScheduledExecutorService connectionExecutor
156 = newScheduledThreadPool(CORE_POOL_SIZE,
157 groupedThreads("onos/netconfdeviceprovider",
158 "connection-executor-%d", log));
Andrea Campanella5c999e22016-03-01 15:12:53 -0800159
Michele Santuari576f09c2016-09-28 14:20:00 +0200160 protected DeviceProviderService providerService;
andreaeb70a942015-10-16 21:34:46 -0700161 private NetconfDeviceListener innerNodeListener = new InnerNetconfDeviceListener();
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700162 private InternalDeviceListener deviceListener = new InternalDeviceListener();
mskala832d0472017-06-09 16:31:42 +0200163 private final Map<DeviceId, AtomicInteger> retriedPortDiscoveryMap = new ConcurrentHashMap<>();
Michele Santuari576f09c2016-09-28 14:20:00 +0200164 protected ScheduledFuture<?> scheduledTask;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530165
Ray Milkey02710432018-02-13 17:08:28 -0800166 protected final ConfigFactory factory =
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700167 // TODO consider moving Config registration to NETCONF ctl bundle
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200168 new ConfigFactory<DeviceId, NetconfDeviceConfig>(
169 SubjectFactories.DEVICE_SUBJECT_FACTORY,
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700170 NetconfDeviceConfig.class, NetconfDeviceConfig.CONFIG_KEY) {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200171 @Override
172 public NetconfDeviceConfig createConfig() {
173 return new NetconfDeviceConfig();
174 }
Ray Milkey02710432018-02-13 17:08:28 -0800175 };
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200176
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);
Ray Milkey02710432018-02-13 17:08:28 -0800188 cfgService.registerConfigFactory(factory);
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 Campanella7d8449b2016-03-02 10:16:42 -0800192 executor.execute(NetconfDeviceProvider.this::connectDevices);
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700193 modified(context);
Thomas Vachuskad6811712015-04-29 21:37:04 -0700194 log.info("Started");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530195 }
196
andreaeb70a942015-10-16 21:34:46 -0700197
Sanjay Se8dcfee2015-04-23 10:07:08 +0530198 @Deactivate
andreaeb70a942015-10-16 21:34:46 -0700199 public void deactivate() {
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700200 componentConfigService.unregisterProperties(getClass(), false);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700201 deviceService.removeListener(deviceListener);
202 active = false;
203 controller.getNetconfDevices().forEach(id -> {
204 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(id.toString()));
205 controller.disconnectDevice(id, true);
206 });
Andrea Campanella86294db2016-03-07 11:42:49 -0800207 controller.removeDeviceListener(innerNodeListener);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700208 deviceService.removeListener(deviceListener);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530209 providerRegistry.unregister(this);
210 providerService = null;
mskala832d0472017-06-09 16:31:42 +0200211 retriedPortDiscoveryMap.clear();
Ray Milkey02710432018-02-13 17:08:28 -0800212 cfgService.unregisterConfigFactory(factory);
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);
mskala832d0472017-06-09 16:31:42 +0200226
227 maxRetries = Tools.getIntegerProperty(properties, "maxRetries",
228 DEFAULT_MAX_RETRIES);
229 log.info("Configured. Number of retries is configured to {} times", maxRetries);
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700230 }
231 if (scheduledTask != null) {
232 scheduledTask.cancel(false);
233 }
234 scheduledTask = schedulePolling();
235 }
236
andreaeb70a942015-10-16 21:34:46 -0700237 public NetconfDeviceProvider() {
Andrea Campanella101417d2015-12-11 17:58:07 -0800238 super(new ProviderId(SCHEME_NAME, DEVICE_PROVIDER_PACKAGE));
Sanjay Se8dcfee2015-04-23 10:07:08 +0530239 }
240
helenyrwufd296b62016-06-22 17:43:02 -0700241 // Checks connection to devices in the config file
242 // every DEFAULT_POLL_FREQUENCY_SECONDS seconds.
243 private ScheduledFuture schedulePolling() {
Yuta HIGUCHIc2b82e32017-03-10 14:33:41 -0800244 return connectionExecutor.scheduleAtFixedRate(exceptionSafe(this::checkAndUpdateDevices),
Thomas Vachuskaf3aaa8d2017-03-31 10:43:58 -0700245 pollFrequency / 10,
246 pollFrequency, TimeUnit.SECONDS);
helenyrwufd296b62016-06-22 17:43:02 -0700247 }
248
Yuta HIGUCHIc2b82e32017-03-10 14:33:41 -0800249 private Runnable exceptionSafe(Runnable runnable) {
250 return new Runnable() {
251
252 @Override
253 public void run() {
254 try {
255 runnable.run();
256 } catch (Exception e) {
257 log.error("Unhandled Exception", e);
258 }
259 }
260 };
261 }
262
Sanjay Se8dcfee2015-04-23 10:07:08 +0530263 @Override
264 public void triggerProbe(DeviceId deviceId) {
andreaeb70a942015-10-16 21:34:46 -0700265 // TODO: This will be implemented later.
Yuta HIGUCHIe4cb8cf2017-05-01 22:18:28 -0700266 log.debug("Should be triggering probe on device {}", deviceId);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530267 }
268
269 @Override
270 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700271 if (active) {
272 switch (newRole) {
273 case MASTER:
274 initiateConnection(deviceId, newRole);
275 log.debug("Accepting mastership role change to {} for device {}", newRole, deviceId);
276 break;
277 case STANDBY:
278 controller.disconnectDevice(deviceId, false);
279 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.STANDBY);
280 //else no-op
281 break;
282 case NONE:
283 controller.disconnectDevice(deviceId, false);
284 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
285 break;
286 default:
287 log.error("Unimplemented Mastership state : {}", newRole);
288
289 }
290 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530291 }
292
293 @Override
294 public boolean isReachable(DeviceId deviceId) {
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700295
296 boolean sessionExists =
297 Optional.ofNullable(controller.getDevicesMap().get(deviceId))
298 .map(NetconfDevice::isActive)
299 .orElse(false);
300 if (sessionExists) {
301 return true;
302 }
303
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700304 //FIXME this is a workaround util device state is shared
305 // between controller instances.
306 Device device = deviceService.getDevice(deviceId);
307 String ip;
308 int port;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700309 if (device != null) {
310 ip = device.annotations().value(IPADDRESS);
311 port = Integer.parseInt(device.annotations().value(PORT));
312 } else {
313 String[] info = deviceId.toString().split(":");
314 if (info.length == 3) {
315 ip = info[1];
316 port = Integer.parseInt(info[2]);
317 } else {
318 ip = Arrays.asList(info).stream().filter(el -> !el.equals(info[0])
319 && !el.equals(info[info.length - 1]))
320 .reduce((t, u) -> t + ":" + u)
321 .get();
322 log.debug("ip v6 {}", ip);
323 port = Integer.parseInt(info[info.length - 1]);
324 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530325 }
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700326 // FIXME just opening TCP session probably is not the appropriate
327 // method to test reachability.
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700328 //test connection to device opening a socket to it.
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700329 log.debug("Testing reachability for {}:{}", ip, port);
Yuta HIGUCHI70c21472017-04-20 20:40:46 -0700330 try (Socket socket = new Socket(ip, port)) {
Yuta HIGUCHI0454d702017-03-17 10:08:38 -0700331 log.debug("rechability of {}, {}, {}", deviceId, socket.isConnected(), !socket.isClosed());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700332 return socket.isConnected() && !socket.isClosed();
333 } catch (IOException e) {
334 log.info("Device {} is not reachable", deviceId);
Yuta HIGUCHI3a50b0d2017-07-10 11:11:54 -0700335 log.debug(" error details", e);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700336 return false;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700337 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530338 }
339
Saurav Dasa2d37502016-03-25 17:50:40 -0700340 @Override
341 public void changePortState(DeviceId deviceId, PortNumber portNumber,
342 boolean enable) {
Andrea Campanella32813682017-10-23 15:29:24 +0200343 Device device = deviceService.getDevice(deviceId);
344 if (mastershipService.isLocalMaster(deviceId)) {
345 if (device.is(PortAdmin.class)) {
346 PortAdmin portAdmin =
347 device.as(PortAdmin.class);
348 CompletableFuture<Boolean> modified;
349 if (enable) {
350 modified = portAdmin.enable(portNumber);
351 } else {
352 modified = portAdmin.disable(portNumber);
353 }
354 modified.thenAccept(result -> {
355 if (result) {
356 Port port = deviceService.getPort(deviceId, portNumber);
357 //rebuilding port description with admin state changed.
358 providerService.portStatusChanged(deviceId,
Yuta HIGUCHI53e47962018-03-01 23:50:48 -0800359 DefaultPortDescription.builder()
360 .withPortNumber(portNumber)
361 .isEnabled(enable)
362 .isRemoved(false)
363 .type(port.type())
364 .portSpeed(port.portSpeed())
365 .annotations((SparseAnnotations) port.annotations())
366 .build());
Andrea Campanella32813682017-10-23 15:29:24 +0200367 } else {
368 log.warn("Your device {} port {} status can't be changed to {}",
369 deviceId, portNumber, enable);
370 }
371 });
372 } else {
373 log.warn("Device {} does not support Port Admin", deviceId);
374 }
375 } else {
376 log.debug("Not master but {}, not changing port state", mastershipService.getLocalRole(deviceId));
377 }
Saurav Dasa2d37502016-03-25 17:50:40 -0700378 }
379
andreaeb70a942015-10-16 21:34:46 -0700380 private class InnerNetconfDeviceListener implements NetconfDeviceListener {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530381
Andrea Campanella101417d2015-12-11 17:58:07 -0800382
andreaeb70a942015-10-16 21:34:46 -0700383 @Override
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700384 public void deviceAdded(DeviceId deviceId) {
385 //no-op
386 log.debug("Netconf device {} added to Netconf subController", deviceId);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530387 }
388
389 @Override
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700390 public void deviceRemoved(DeviceId deviceId) {
391 Preconditions.checkNotNull(deviceId, ISNULL);
helenyrwufd296b62016-06-22 17:43:02 -0700392
393 if (deviceService.getDevice(deviceId) != null) {
394 providerService.deviceDisconnected(deviceId);
mskala832d0472017-06-09 16:31:42 +0200395 retriedPortDiscoveryMap.remove(deviceId);
helenyrwufd296b62016-06-22 17:43:02 -0700396 log.debug("Netconf device {} removed from Netconf subController", deviceId);
397 } else {
398 log.warn("Netconf device {} does not exist in the store, " +
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530399 "it may already have been removed", deviceId);
helenyrwufd296b62016-06-22 17:43:02 -0700400 }
andreaeb70a942015-10-16 21:34:46 -0700401 }
402 }
403
andreaeb70a942015-10-16 21:34:46 -0700404 private void connectDevices() {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200405 Set<DeviceId> deviceSubjects =
406 cfgService.getSubjects(DeviceId.class, NetconfDeviceConfig.class);
407 deviceSubjects.forEach(deviceId -> {
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700408 connectDevice(cfgService.getConfig(deviceId, NetconfDeviceConfig.class));
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200409 });
helenyrwufd296b62016-06-22 17:43:02 -0700410 }
411
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700412
413 private void connectDevice(NetconfDeviceConfig config) {
414 if (config == null) {
415 return;
416 }
417 DeviceId deviceId = config.subject();
418 if (!deviceId.uri().getScheme().equals(SCHEME_NAME)) {
419 // not under my scheme, skipping
420 log.trace("{} not my scheme, skipping", deviceId);
421 return;
422 }
423 DeviceDescription deviceDescription = createDeviceRepresentation(deviceId, config);
424 log.debug("Connecting NETCONF device {}, on {}:{} with username {}",
425 deviceId, config.ip(), config.port(), config.username());
426 storeDeviceKey(config.sshKey(), config.username(), config.password(), deviceId);
Andrea Campanella105736e2017-11-23 12:52:43 +0100427 retriedPortDiscoveryMap.put(deviceId, new AtomicInteger(0));
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700428 if (deviceService.getDevice(deviceId) == null) {
429 providerService.deviceConnected(deviceId, deviceDescription);
430 }
431 try {
Andrea Campanella105736e2017-11-23 12:52:43 +0100432 checkAndUpdateDevice(deviceId, deviceDescription, true);
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700433 } catch (Exception e) {
434 log.error("Unhandled exception checking {}", deviceId, e);
435 }
436 }
437
Andrea Campanella105736e2017-11-23 12:52:43 +0100438 private void checkAndUpdateDevice(DeviceId deviceId, DeviceDescription deviceDescription, boolean newlyConnected) {
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530439 Device device = deviceService.getDevice(deviceId);
440 if (device == null) {
Andrea Campanella105736e2017-11-23 12:52:43 +0100441 log.debug("Device {} has not been added to store, since it's not reachable", deviceId);
442 return;
443 }
444 boolean isReachable = isReachable(deviceId);
445 if (!isReachable && deviceService.isAvailable(deviceId)) {
446 providerService.deviceDisconnected(deviceId);
447 return;
448 } else if (newlyConnected) {
449 updateDeviceDescription(deviceId, deviceDescription, device);
450 }
451 if (isReachable && deviceService.isAvailable(deviceId) &&
452 mastershipService.isLocalMaster(deviceId)) {
453 //if ports are not discovered, retry the discovery
454 if (deviceService.getPorts(deviceId).isEmpty() &&
455 retriedPortDiscoveryMap.get(deviceId).getAndIncrement() < maxRetries) {
456 discoverPorts(deviceId);
457 }
458 updatePortStatistics(device);
459 }
460 }
461
462 private void updateDeviceDescription(DeviceId deviceId, DeviceDescription deviceDescription, Device device) {
463 if (device.is(DeviceDescriptionDiscovery.class)) {
464 if (mastershipService.isLocalMaster(deviceId)) {
465 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
466 device.as(DeviceDescriptionDiscovery.class);
467 DeviceDescription updatedDeviceDescription =
468 deviceDescriptionDiscovery.discoverDeviceDetails();
469 if (updatedDeviceDescription != null &&
470 !descriptionEquals(device, updatedDeviceDescription)) {
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530471 providerService.deviceConnected(
472 deviceId, new DefaultDeviceDescription(
Andrea Campanella105736e2017-11-23 12:52:43 +0100473 updatedDeviceDescription, true,
474 updatedDeviceDescription.annotations()));
475 } else if (updatedDeviceDescription == null) {
476 providerService.deviceConnected(
477 deviceId, new DefaultDeviceDescription(
478 deviceDescription, true,
479 deviceDescription.annotations()));
Konstantinos Kanonakis4d67dd82016-08-05 12:18:52 -0500480 }
helenyrwufd296b62016-06-22 17:43:02 -0700481 }
Andrea Campanella105736e2017-11-23 12:52:43 +0100482 } else {
483 log.warn("No DeviceDescriptionDiscovery behaviour for device {} " +
484 "using DefaultDeviceDescription", deviceId);
485 providerService.deviceConnected(
486 deviceId, new DefaultDeviceDescription(
487 deviceDescription, true, deviceDescription.annotations()));
helenyrwufd296b62016-06-22 17:43:02 -0700488 }
489 }
490
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530491 private void updatePortStatistics(Device device) {
492 if (device.is(PortStatisticsDiscovery.class)) {
493 PortStatisticsDiscovery d = device.as(PortStatisticsDiscovery.class);
Andrea Campanellac3627842017-04-04 18:06:54 +0200494 Collection<PortStatistics> portStatistics = d.discoverPortStatistics();
495 if (portStatistics != null) {
496 providerService.updatePortStatistics(device.id(),
497 portStatistics);
498 }
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530499 } else {
Yuta HIGUCHIe4cb8cf2017-05-01 22:18:28 -0700500 log.debug("No port statistics getter behaviour for device {}",
501 device.id());
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530502 }
503 }
504
Michele Santuari00cc1f72016-09-08 17:05:24 +0200505 private boolean descriptionEquals(Device device, DeviceDescription updatedDeviceDescription) {
Yuta HIGUCHIf381fc72017-01-03 10:39:36 -0800506 return Objects.equal(device.id().uri(), updatedDeviceDescription.deviceUri())
Michele Santuari00cc1f72016-09-08 17:05:24 +0200507 && Objects.equal(device.type(), updatedDeviceDescription.type())
508 && Objects.equal(device.manufacturer(), updatedDeviceDescription.manufacturer())
509 && Objects.equal(device.hwVersion(), updatedDeviceDescription.hwVersion())
510 && Objects.equal(device.swVersion(), updatedDeviceDescription.swVersion())
511 && Objects.equal(device.serialNumber(), updatedDeviceDescription.serialNumber())
512 && Objects.equal(device.chassisId(), updatedDeviceDescription.chassisId())
513 && Objects.equal(device.annotations(), updatedDeviceDescription.annotations());
514 }
515
helenyrwufd296b62016-06-22 17:43:02 -0700516 private void checkAndUpdateDevices() {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200517 Set<DeviceId> deviceSubjects =
518 cfgService.getSubjects(DeviceId.class, NetconfDeviceConfig.class);
519 deviceSubjects.forEach(deviceId -> {
520 NetconfDeviceConfig config =
521 cfgService.getConfig(deviceId, NetconfDeviceConfig.class);
522 DeviceDescription deviceDescription = createDeviceRepresentation(deviceId, config);
523 storeDeviceKey(config.sshKey(), config.username(), config.password(), deviceId);
Andrea Campanella105736e2017-11-23 12:52:43 +0100524 checkAndUpdateDevice(deviceId, deviceDescription, false);
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200525 });
andreaeb70a942015-10-16 21:34:46 -0700526 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530527
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200528 private DeviceDescription createDeviceRepresentation(DeviceId deviceId, NetconfDeviceConfig config) {
529 Preconditions.checkNotNull(deviceId, ISNULL);
530 //Netconf configuration object
531 ChassisId cid = new ChassisId();
532 String ipAddress = config.ip().toString();
533 SparseAnnotations annotations = DefaultAnnotations.builder()
534 .set(IPADDRESS, ipAddress)
535 .set(PORT, String.valueOf(config.port()))
536 .set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase())
537 .build();
538 return new DefaultDeviceDescription(
539 deviceId.uri(),
540 Device.Type.SWITCH,
541 UNKNOWN, UNKNOWN,
542 UNKNOWN, UNKNOWN,
543 cid, false,
544 annotations);
545 }
546
547 private void storeDeviceKey(String sshKey, String username, String password, DeviceId deviceId) {
548 if (sshKey.equals("")) {
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600549 deviceKeyAdminService.addKey(
550 DeviceKey.createDeviceKeyUsingUsernamePassword(
551 DeviceKeyId.deviceKeyId(deviceId.toString()),
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200552 null, username, password));
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600553 } else {
554 deviceKeyAdminService.addKey(
555 DeviceKey.createDeviceKeyUsingSshKey(
556 DeviceKeyId.deviceKeyId(deviceId.toString()),
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200557 null, username, password,
558 sshKey));
Himanshu Ranjan7c2ee3c2017-02-13 05:10:08 -0600559 }
560 }
561
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700562 private void initiateConnection(DeviceId deviceId, MastershipRole newRole) {
563 try {
564 if (isReachable(deviceId)) {
565 controller.connectDevice(deviceId);
566 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.MASTER);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700567 }
568 } catch (Exception e) {
569 if (deviceService.getDevice(deviceId) != null) {
570 providerService.deviceDisconnected(deviceId);
571 }
572 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(deviceId.toString()));
Ray Milkey986a47a2018-01-25 11:38:51 -0800573 throw new IllegalStateException(new NetconfException(
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200574 "Can't connect to NETCONF device " + deviceId, e));
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700575
576 }
577 }
578
579 private void discoverPorts(DeviceId deviceId) {
580 Device device = deviceService.getDevice(deviceId);
Andrea Campanella6c71a052016-04-22 11:56:31 -0700581 //TODO remove when PortDiscovery is removed from master
Ray Milkey640fedd2018-02-08 09:02:26 -0800582 if (device.is(DeviceDescriptionDiscovery.class)) {
Andrea Campanella6c71a052016-04-22 11:56:31 -0700583 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
584 device.as(DeviceDescriptionDiscovery.class);
585 providerService.updatePorts(deviceId,
586 deviceDescriptionDiscovery.discoverPortDetails());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700587 } else {
588 log.warn("No portGetter behaviour for device {}", deviceId);
589 }
Gaurav Agrawaldab4d772017-03-29 15:15:13 +0530590
591 // Port statistics discovery
Vidyashree Ramad89a1532017-03-30 15:13:52 +0530592 updatePortStatistics(device);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700593 }
594
595 /**
596 * Return the DeviceId about the device containing the URI.
597 *
Andrea Campanella6c71a052016-04-22 11:56:31 -0700598 * @param ip IP address
Ray Milkeyd4334db2016-04-05 17:39:44 -0700599 * @param port port number
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700600 * @return DeviceId
601 */
602 public DeviceId getDeviceId(String ip, int port) {
603 try {
604 return DeviceId.deviceId(new URI(NETCONF, ip + ":" + port, null));
605 } catch (URISyntaxException e) {
606 throw new IllegalArgumentException("Unable to build deviceID for device "
607 + ip + ":" + port, e);
608 }
609 }
610
611 /**
612 * Listener for configuration events.
613 */
andreaeb70a942015-10-16 21:34:46 -0700614 private class InternalNetworkConfigListener implements NetworkConfigListener {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530615
andreaeb70a942015-10-16 21:34:46 -0700616
617 @Override
618 public void event(NetworkConfigEvent event) {
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200619 if (event.configClass().equals(NetconfDeviceConfig.class)) {
Yuta HIGUCHIb6e0e912017-05-18 20:13:52 -0700620 executor.execute(() -> connectDevice((NetconfDeviceConfig) event.config().get()));
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200621 } else {
622 log.warn("Injecting device via this Json is deprecated, " +
623 "please put configuration under devices/ as shown in the wiki");
Andrea Campanella34cf65c2017-04-12 13:51:32 +0200624 }
625
Sanjay Se8dcfee2015-04-23 10:07:08 +0530626 }
627
andreaeb70a942015-10-16 21:34:46 -0700628 @Override
629 public boolean isRelevant(NetworkConfigEvent event) {
Ray Milkey02710432018-02-13 17:08:28 -0800630 return (event.configClass().equals(NetconfDeviceConfig.class)) &&
andreaeb70a942015-10-16 21:34:46 -0700631 (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
632 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530633 }
634 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700635
636 /**
637 * Listener for core device events.
638 */
639 private class InternalDeviceListener implements DeviceListener {
640 @Override
641 public void event(DeviceEvent event) {
642 if ((event.type() == DeviceEvent.Type.DEVICE_ADDED)) {
643 executor.execute(() -> discoverPorts(event.subject().id()));
644 } else if ((event.type() == DeviceEvent.Type.DEVICE_REMOVED)) {
645 log.debug("removing device {}", event.subject().id());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700646 controller.disconnectDevice(event.subject().id(), true);
647 }
648 }
649
650 @Override
651 public boolean isRelevant(DeviceEvent event) {
652 if (mastershipService.getMasterFor(event.subject().id()) == null) {
653 return true;
654 }
Andrea Campanella968f93f2017-06-08 11:09:28 +0200655 return (SCHEME_NAME.equalsIgnoreCase(event.subject().annotations().value(AnnotationKeys.PROTOCOL)) ||
656 (SCHEME_NAME.equalsIgnoreCase(event.subject().id().uri().getScheme()))) &&
Michele Santuari576f09c2016-09-28 14:20:00 +0200657 mastershipService.isLocalMaster(event.subject().id());
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700658 }
659 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530660}