blob: 49702969fcd870938370df1829ef223db6e4f549 [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
andreaeb70a942015-10-16 21:34:46 -070019import com.google.common.base.Preconditions;
Sanjay Se8dcfee2015-04-23 10:07:08 +053020import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
Sanjay Se8dcfee2015-04-23 10:07:08 +053023import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
25import org.onlab.packet.ChassisId;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070026import org.onosproject.cluster.ClusterService;
27import org.onosproject.cluster.NodeId;
andreaeb70a942015-10-16 21:34:46 -070028import org.onosproject.core.ApplicationId;
29import org.onosproject.core.CoreService;
30import org.onosproject.incubator.net.config.basics.ConfigException;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070031import org.onosproject.mastership.MastershipService;
Marc De Leenheerb0d131c2016-03-01 20:34:59 -080032import org.onosproject.net.AnnotationKeys;
andreaeb70a942015-10-16 21:34:46 -070033import org.onosproject.net.DefaultAnnotations;
Sanjay Se8dcfee2015-04-23 10:07:08 +053034import org.onosproject.net.Device;
35import org.onosproject.net.DeviceId;
36import org.onosproject.net.MastershipRole;
Saurav Dasa2d37502016-03-25 17:50:40 -070037import org.onosproject.net.PortNumber;
andreaeb70a942015-10-16 21:34:46 -070038import org.onosproject.net.SparseAnnotations;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -080039import org.onosproject.net.behaviour.PortDiscovery;
andreaeb70a942015-10-16 21:34:46 -070040import org.onosproject.net.config.ConfigFactory;
41import org.onosproject.net.config.NetworkConfigEvent;
42import org.onosproject.net.config.NetworkConfigListener;
43import org.onosproject.net.config.NetworkConfigRegistry;
Sanjay Se8dcfee2015-04-23 10:07:08 +053044import org.onosproject.net.device.DefaultDeviceDescription;
45import org.onosproject.net.device.DeviceDescription;
Andrea Campanella6c71a052016-04-22 11:56:31 -070046import org.onosproject.net.device.DeviceDescriptionDiscovery;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070047import org.onosproject.net.device.DeviceEvent;
48import org.onosproject.net.device.DeviceListener;
Sanjay Se8dcfee2015-04-23 10:07:08 +053049import org.onosproject.net.device.DeviceProvider;
50import org.onosproject.net.device.DeviceProviderRegistry;
51import org.onosproject.net.device.DeviceProviderService;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -080052import org.onosproject.net.device.DeviceService;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070053import org.onosproject.net.key.DeviceKey;
54import org.onosproject.net.key.DeviceKeyAdminService;
55import org.onosproject.net.key.DeviceKeyId;
Sanjay Se8dcfee2015-04-23 10:07:08 +053056import org.onosproject.net.provider.AbstractProvider;
57import org.onosproject.net.provider.ProviderId;
andreaeb70a942015-10-16 21:34:46 -070058import org.onosproject.netconf.NetconfController;
andreaeb70a942015-10-16 21:34:46 -070059import org.onosproject.netconf.NetconfDeviceListener;
Andrea Campanella8b1cb672016-01-25 13:58:58 -080060import org.onosproject.netconf.NetconfException;
Sanjay Se8dcfee2015-04-23 10:07:08 +053061import org.slf4j.Logger;
62
Andrea Campanella087ceb92015-12-07 09:58:34 -080063import java.io.IOException;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070064import java.net.Socket;
65import java.net.URI;
66import java.net.URISyntaxException;
67import java.util.Arrays;
Andrea Campanella5c999e22016-03-01 15:12:53 -080068import java.util.concurrent.ExecutorService;
69import java.util.concurrent.Executors;
andreaeb70a942015-10-16 21:34:46 -070070
Andrea Campanella5c999e22016-03-01 15:12:53 -080071import static org.onlab.util.Tools.groupedThreads;
andreaeb70a942015-10-16 21:34:46 -070072import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
73import static org.slf4j.LoggerFactory.getLogger;
74
Sanjay Se8dcfee2015-04-23 10:07:08 +053075/**
andreaeb70a942015-10-16 21:34:46 -070076 * Provider which uses an NETCONF controller to detect device.
Sanjay Se8dcfee2015-04-23 10:07:08 +053077 */
78@Component(immediate = true)
79public class NetconfDeviceProvider extends AbstractProvider
80 implements DeviceProvider {
Andrea Campanella7e6200a2016-03-21 09:48:40 -070081
82 public static final String ACTIVE = "active";
andreaeb70a942015-10-16 21:34:46 -070083 private final Logger log = getLogger(getClass());
Sanjay Se8dcfee2015-04-23 10:07:08 +053084
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected DeviceProviderRegistry providerRegistry;
87
andreaeb70a942015-10-16 21:34:46 -070088 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella101417d2015-12-11 17:58:07 -080089 protected NetconfController controller;
Sanjay Se8dcfee2015-04-23 10:07:08 +053090
91 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
andreaeb70a942015-10-16 21:34:46 -070092 protected NetworkConfigRegistry cfgService;
Sanjay Se8dcfee2015-04-23 10:07:08 +053093
Thomas Vachuskad6811712015-04-29 21:37:04 -070094 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
andreaeb70a942015-10-16 21:34:46 -070095 protected CoreService coreService;
Thomas Vachuskad6811712015-04-29 21:37:04 -070096
Aaron Kruglikov17b4c852016-01-15 16:37:04 -080097 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -070098 protected DeviceService deviceService;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -080099
100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700101 protected DeviceKeyAdminService deviceKeyAdminService;
102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
104 protected MastershipService mastershipService;
105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
107 protected ClusterService clusterService;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -0800108
Andrea Campanella101417d2015-12-11 17:58:07 -0800109 private static final String APP_NAME = "org.onosproject.netconf";
110 private static final String SCHEME_NAME = "netconf";
111 private static final String DEVICE_PROVIDER_PACKAGE = "org.onosproject.netconf.provider.device";
112 private static final String UNKNOWN = "unknown";
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700113 protected static final String ISNULL = "NetconfDeviceInfo is null";
114 private static final String IPADDRESS = "ipaddress";
115 private static final String NETCONF = "netconf";
116 private static final String PORT = "port";
117 //FIXME eventually a property
118 private static final int ISREACHABLE_TIMEOUT = 2000;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530119
Andrea Campanella5c999e22016-03-01 15:12:53 -0800120 private final ExecutorService executor =
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700121 Executors.newFixedThreadPool(5, groupedThreads("onos/netconfdeviceprovider",
122 "device-installer-%d", log));
Andrea Campanella5c999e22016-03-01 15:12:53 -0800123
andreaeb70a942015-10-16 21:34:46 -0700124 private DeviceProviderService providerService;
125 private NetconfDeviceListener innerNodeListener = new InnerNetconfDeviceListener();
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700126 private InternalDeviceListener deviceListener = new InternalDeviceListener();
127 private NodeId localNodeId;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530128
andreaeb70a942015-10-16 21:34:46 -0700129 private final ConfigFactory factory =
130 new ConfigFactory<ApplicationId, NetconfProviderConfig>(APP_SUBJECT_FACTORY,
131 NetconfProviderConfig.class,
132 "devices",
133 true) {
134 @Override
135 public NetconfProviderConfig createConfig() {
136 return new NetconfProviderConfig();
137 }
138 };
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700139 private final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
andreaeb70a942015-10-16 21:34:46 -0700140 private ApplicationId appId;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700141 private boolean active;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530142
Sanjay Se8dcfee2015-04-23 10:07:08 +0530143
144 @Activate
andreaeb70a942015-10-16 21:34:46 -0700145 public void activate() {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700146 active = true;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530147 providerService = providerRegistry.register(this);
Andrea Campanella101417d2015-12-11 17:58:07 -0800148 appId = coreService.registerApplication(APP_NAME);
andreaeb70a942015-10-16 21:34:46 -0700149 cfgService.registerConfigFactory(factory);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700150 cfgService.addListener(cfgListener);
andreaeb70a942015-10-16 21:34:46 -0700151 controller.addDeviceListener(innerNodeListener);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700152 deviceService.addListener(deviceListener);
Andrea Campanella7d8449b2016-03-02 10:16:42 -0800153 executor.execute(NetconfDeviceProvider.this::connectDevices);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700154 localNodeId = clusterService.getLocalNode().id();
Thomas Vachuskad6811712015-04-29 21:37:04 -0700155 log.info("Started");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530156 }
157
andreaeb70a942015-10-16 21:34:46 -0700158
Sanjay Se8dcfee2015-04-23 10:07:08 +0530159 @Deactivate
andreaeb70a942015-10-16 21:34:46 -0700160 public void deactivate() {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700161 deviceService.removeListener(deviceListener);
162 active = false;
163 controller.getNetconfDevices().forEach(id -> {
164 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(id.toString()));
165 controller.disconnectDevice(id, true);
166 });
Andrea Campanella86294db2016-03-07 11:42:49 -0800167 controller.removeDeviceListener(innerNodeListener);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700168 deviceService.removeListener(deviceListener);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530169 providerRegistry.unregister(this);
170 providerService = null;
andreaeb70a942015-10-16 21:34:46 -0700171 cfgService.unregisterConfigFactory(factory);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700172 executor.shutdown();
Sanjay Seb5eebb2015-04-24 15:44:50 +0530173 log.info("Stopped");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530174 }
175
andreaeb70a942015-10-16 21:34:46 -0700176 public NetconfDeviceProvider() {
Andrea Campanella101417d2015-12-11 17:58:07 -0800177 super(new ProviderId(SCHEME_NAME, DEVICE_PROVIDER_PACKAGE));
Sanjay Se8dcfee2015-04-23 10:07:08 +0530178 }
179
180 @Override
181 public void triggerProbe(DeviceId deviceId) {
andreaeb70a942015-10-16 21:34:46 -0700182 // TODO: This will be implemented later.
183 log.info("Triggering probe on device {}", deviceId);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530184 }
185
186 @Override
187 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700188 if (active) {
189 switch (newRole) {
190 case MASTER:
191 initiateConnection(deviceId, newRole);
192 log.debug("Accepting mastership role change to {} for device {}", newRole, deviceId);
193 break;
194 case STANDBY:
195 controller.disconnectDevice(deviceId, false);
196 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.STANDBY);
197 //else no-op
198 break;
199 case NONE:
200 controller.disconnectDevice(deviceId, false);
201 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
202 break;
203 default:
204 log.error("Unimplemented Mastership state : {}", newRole);
205
206 }
207 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530208 }
209
210 @Override
211 public boolean isReachable(DeviceId deviceId) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700212 //FIXME this is a workaround util device state is shared
213 // between controller instances.
214 Device device = deviceService.getDevice(deviceId);
215 String ip;
216 int port;
217 Socket socket = null;
218 if (device != null) {
219 ip = device.annotations().value(IPADDRESS);
220 port = Integer.parseInt(device.annotations().value(PORT));
221 } else {
222 String[] info = deviceId.toString().split(":");
223 if (info.length == 3) {
224 ip = info[1];
225 port = Integer.parseInt(info[2]);
226 } else {
227 ip = Arrays.asList(info).stream().filter(el -> !el.equals(info[0])
228 && !el.equals(info[info.length - 1]))
229 .reduce((t, u) -> t + ":" + u)
230 .get();
231 log.debug("ip v6 {}", ip);
232 port = Integer.parseInt(info[info.length - 1]);
233 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530234 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700235 //test connection to device opening a socket to it.
236 try {
237 socket = new Socket(ip, port);
238 log.debug("rechability of {}, {}, {}", deviceId, socket.isConnected() && !socket.isClosed());
239 return socket.isConnected() && !socket.isClosed();
240 } catch (IOException e) {
241 log.info("Device {} is not reachable", deviceId);
242 return false;
243 } finally {
244 if (socket != null) {
245 try {
246 socket.close();
247 } catch (IOException e) {
248 log.debug("Test Socket failed {} ", deviceId);
249 return false;
250 }
251 }
252 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530253 }
254
Saurav Dasa2d37502016-03-25 17:50:40 -0700255 @Override
256 public void changePortState(DeviceId deviceId, PortNumber portNumber,
257 boolean enable) {
258 // TODO if required
259 }
260
andreaeb70a942015-10-16 21:34:46 -0700261 private class InnerNetconfDeviceListener implements NetconfDeviceListener {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530262
Andrea Campanella101417d2015-12-11 17:58:07 -0800263
andreaeb70a942015-10-16 21:34:46 -0700264 @Override
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700265 public void deviceAdded(DeviceId deviceId) {
266 //no-op
267 log.debug("Netconf device {} added to Netconf subController", deviceId);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530268 }
269
270 @Override
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700271 public void deviceRemoved(DeviceId deviceId) {
272 Preconditions.checkNotNull(deviceId, ISNULL);
273 log.debug("Netconf device {} removed from Netconf subController", deviceId);
andreaeb70a942015-10-16 21:34:46 -0700274 providerService.deviceDisconnected(deviceId);
andreaeb70a942015-10-16 21:34:46 -0700275 }
276 }
277
andreaeb70a942015-10-16 21:34:46 -0700278 private void connectDevices() {
279 NetconfProviderConfig cfg = cfgService.getConfig(appId, NetconfProviderConfig.class);
280 if (cfg != null) {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530281 try {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700282 cfg.getDevicesAddresses().stream().forEach(addr -> {
283 DeviceId deviceId = getDeviceId(addr.ip().toString(), addr.port());
284 Preconditions.checkNotNull(deviceId, ISNULL);
285 //Netconf configuration object
286 ChassisId cid = new ChassisId();
287 String ipAddress = addr.ip().toString();
288 SparseAnnotations annotations = DefaultAnnotations.builder()
289 .set(IPADDRESS, ipAddress)
290 .set(PORT, String.valueOf(addr.port()))
291 .set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase())
292 .build();
293 DeviceDescription deviceDescription = new DefaultDeviceDescription(
294 deviceId.uri(),
295 Device.Type.SWITCH,
296 UNKNOWN, UNKNOWN,
297 UNKNOWN, UNKNOWN,
298 cid,
299 annotations);
300 deviceKeyAdminService.addKey(
301 DeviceKey.createDeviceKeyUsingUsernamePassword(
302 DeviceKeyId.deviceKeyId(deviceId.toString()),
303 null, addr.name(), addr.password()));
304 providerService.deviceConnected(deviceId, deviceDescription);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700305 });
andreaeb70a942015-10-16 21:34:46 -0700306 } catch (ConfigException e) {
307 log.error("Cannot read config error " + e);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530308 }
309 }
andreaeb70a942015-10-16 21:34:46 -0700310 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530311
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700312 private void initiateConnection(DeviceId deviceId, MastershipRole newRole) {
313 try {
314 if (isReachable(deviceId)) {
315 controller.connectDevice(deviceId);
316 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.MASTER);
317 } else {
318 return;
319 }
320 } catch (Exception e) {
321 if (deviceService.getDevice(deviceId) != null) {
322 providerService.deviceDisconnected(deviceId);
323 }
324 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(deviceId.toString()));
325 throw new RuntimeException(new NetconfException(
326 "Can't connect to NETCONF " + "device on " + deviceId + ":" + deviceId, e));
327
328 }
329 }
330
331 private void discoverPorts(DeviceId deviceId) {
332 Device device = deviceService.getDevice(deviceId);
Andrea Campanella6c71a052016-04-22 11:56:31 -0700333 //TODO remove when PortDiscovery is removed from master
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700334 if (device.is(PortDiscovery.class)) {
335 PortDiscovery portConfig = device.as(PortDiscovery.class);
336 providerService.updatePorts(deviceId,
337 portConfig.getPorts());
Andrea Campanella6c71a052016-04-22 11:56:31 -0700338 } else if (device.is(DeviceDescriptionDiscovery.class)) {
339 DeviceDescriptionDiscovery deviceDescriptionDiscovery =
340 device.as(DeviceDescriptionDiscovery.class);
341 providerService.updatePorts(deviceId,
342 deviceDescriptionDiscovery.discoverPortDetails());
343
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700344 } else {
345 log.warn("No portGetter behaviour for device {}", deviceId);
346 }
347 }
348
349 /**
350 * Return the DeviceId about the device containing the URI.
351 *
Andrea Campanella6c71a052016-04-22 11:56:31 -0700352 * @param ip IP address
Ray Milkeyd4334db2016-04-05 17:39:44 -0700353 * @param port port number
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700354 * @return DeviceId
355 */
356 public DeviceId getDeviceId(String ip, int port) {
357 try {
358 return DeviceId.deviceId(new URI(NETCONF, ip + ":" + port, null));
359 } catch (URISyntaxException e) {
360 throw new IllegalArgumentException("Unable to build deviceID for device "
361 + ip + ":" + port, e);
362 }
363 }
364
365 /**
366 * Listener for configuration events.
367 */
andreaeb70a942015-10-16 21:34:46 -0700368 private class InternalNetworkConfigListener implements NetworkConfigListener {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530369
andreaeb70a942015-10-16 21:34:46 -0700370
371 @Override
372 public void event(NetworkConfigEvent event) {
Andrea Campanella90f044f2016-03-02 09:14:57 -0800373 executor.execute(NetconfDeviceProvider.this::connectDevices);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530374 }
375
andreaeb70a942015-10-16 21:34:46 -0700376 @Override
377 public boolean isRelevant(NetworkConfigEvent event) {
andreaeb70a942015-10-16 21:34:46 -0700378 return event.configClass().equals(NetconfProviderConfig.class) &&
379 (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
380 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530381 }
382 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700383
384 /**
385 * Listener for core device events.
386 */
387 private class InternalDeviceListener implements DeviceListener {
388 @Override
389 public void event(DeviceEvent event) {
390 if ((event.type() == DeviceEvent.Type.DEVICE_ADDED)) {
391 executor.execute(() -> discoverPorts(event.subject().id()));
392 } else if ((event.type() == DeviceEvent.Type.DEVICE_REMOVED)) {
393 log.debug("removing device {}", event.subject().id());
394 deviceService.getDevice(event.subject().id()).annotations().keys();
395 controller.disconnectDevice(event.subject().id(), true);
396 }
397 }
398
399 @Override
400 public boolean isRelevant(DeviceEvent event) {
401 if (mastershipService.getMasterFor(event.subject().id()) == null) {
402 return true;
403 }
404 return event.subject().annotations().value(AnnotationKeys.PROTOCOL)
405 .equals(SCHEME_NAME.toUpperCase()) &&
406 mastershipService.getMasterFor(event.subject().id()).equals(localNodeId);
407 }
408 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530409}