blob: 1ad4025725abee1c6bb1c9a4414eebe0d590ad1a [file] [log] [blame]
Sanjay Se8dcfee2015-04-23 10:07:08 +05301/*
2 * Copyright 2015 Open Networking Laboratory
3 *
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 Campanella7e6200a2016-03-21 09:48:40 -070046import org.onosproject.net.device.DeviceEvent;
47import org.onosproject.net.device.DeviceListener;
Sanjay Se8dcfee2015-04-23 10:07:08 +053048import org.onosproject.net.device.DeviceProvider;
49import org.onosproject.net.device.DeviceProviderRegistry;
50import org.onosproject.net.device.DeviceProviderService;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -080051import org.onosproject.net.device.DeviceService;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070052import org.onosproject.net.key.DeviceKey;
53import org.onosproject.net.key.DeviceKeyAdminService;
54import org.onosproject.net.key.DeviceKeyId;
Sanjay Se8dcfee2015-04-23 10:07:08 +053055import org.onosproject.net.provider.AbstractProvider;
56import org.onosproject.net.provider.ProviderId;
andreaeb70a942015-10-16 21:34:46 -070057import org.onosproject.netconf.NetconfController;
andreaeb70a942015-10-16 21:34:46 -070058import org.onosproject.netconf.NetconfDeviceListener;
Andrea Campanella8b1cb672016-01-25 13:58:58 -080059import org.onosproject.netconf.NetconfException;
Sanjay Se8dcfee2015-04-23 10:07:08 +053060import org.slf4j.Logger;
61
Andrea Campanella087ceb92015-12-07 09:58:34 -080062import java.io.IOException;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070063import java.net.Socket;
64import java.net.URI;
65import java.net.URISyntaxException;
66import java.util.Arrays;
Andrea Campanella5c999e22016-03-01 15:12:53 -080067import java.util.concurrent.ExecutorService;
68import java.util.concurrent.Executors;
andreaeb70a942015-10-16 21:34:46 -070069
Andrea Campanella5c999e22016-03-01 15:12:53 -080070import static org.onlab.util.Tools.groupedThreads;
andreaeb70a942015-10-16 21:34:46 -070071import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
72import static org.slf4j.LoggerFactory.getLogger;
73
Sanjay Se8dcfee2015-04-23 10:07:08 +053074/**
andreaeb70a942015-10-16 21:34:46 -070075 * Provider which uses an NETCONF controller to detect device.
Sanjay Se8dcfee2015-04-23 10:07:08 +053076 */
77@Component(immediate = true)
78public class NetconfDeviceProvider extends AbstractProvider
79 implements DeviceProvider {
Andrea Campanella7e6200a2016-03-21 09:48:40 -070080
81 public static final String ACTIVE = "active";
andreaeb70a942015-10-16 21:34:46 -070082 private final Logger log = getLogger(getClass());
Sanjay Se8dcfee2015-04-23 10:07:08 +053083
84 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
85 protected DeviceProviderRegistry providerRegistry;
86
andreaeb70a942015-10-16 21:34:46 -070087 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella101417d2015-12-11 17:58:07 -080088 protected NetconfController controller;
Sanjay Se8dcfee2015-04-23 10:07:08 +053089
90 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
andreaeb70a942015-10-16 21:34:46 -070091 protected NetworkConfigRegistry cfgService;
Sanjay Se8dcfee2015-04-23 10:07:08 +053092
Thomas Vachuskad6811712015-04-29 21:37:04 -070093 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
andreaeb70a942015-10-16 21:34:46 -070094 protected CoreService coreService;
Thomas Vachuskad6811712015-04-29 21:37:04 -070095
Aaron Kruglikov17b4c852016-01-15 16:37:04 -080096 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -070097 protected DeviceService deviceService;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -080098
99 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700100 protected DeviceKeyAdminService deviceKeyAdminService;
101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected MastershipService mastershipService;
104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected ClusterService clusterService;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -0800107
Andrea Campanella101417d2015-12-11 17:58:07 -0800108 private static final String APP_NAME = "org.onosproject.netconf";
109 private static final String SCHEME_NAME = "netconf";
110 private static final String DEVICE_PROVIDER_PACKAGE = "org.onosproject.netconf.provider.device";
111 private static final String UNKNOWN = "unknown";
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700112 protected static final String ISNULL = "NetconfDeviceInfo is null";
113 private static final String IPADDRESS = "ipaddress";
114 private static final String NETCONF = "netconf";
115 private static final String PORT = "port";
116 //FIXME eventually a property
117 private static final int ISREACHABLE_TIMEOUT = 2000;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530118
Andrea Campanella5c999e22016-03-01 15:12:53 -0800119 private final ExecutorService executor =
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700120 Executors.newFixedThreadPool(5, groupedThreads("onos/netconfdeviceprovider",
121 "device-installer-%d", log));
Andrea Campanella5c999e22016-03-01 15:12:53 -0800122
andreaeb70a942015-10-16 21:34:46 -0700123 private DeviceProviderService providerService;
124 private NetconfDeviceListener innerNodeListener = new InnerNetconfDeviceListener();
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700125 private InternalDeviceListener deviceListener = new InternalDeviceListener();
126 private NodeId localNodeId;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530127
andreaeb70a942015-10-16 21:34:46 -0700128 private final ConfigFactory factory =
129 new ConfigFactory<ApplicationId, NetconfProviderConfig>(APP_SUBJECT_FACTORY,
130 NetconfProviderConfig.class,
131 "devices",
132 true) {
133 @Override
134 public NetconfProviderConfig createConfig() {
135 return new NetconfProviderConfig();
136 }
137 };
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700138 private final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
andreaeb70a942015-10-16 21:34:46 -0700139 private ApplicationId appId;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700140 private boolean active;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530141
Sanjay Se8dcfee2015-04-23 10:07:08 +0530142
143 @Activate
andreaeb70a942015-10-16 21:34:46 -0700144 public void activate() {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700145 active = true;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530146 providerService = providerRegistry.register(this);
Andrea Campanella101417d2015-12-11 17:58:07 -0800147 appId = coreService.registerApplication(APP_NAME);
andreaeb70a942015-10-16 21:34:46 -0700148 cfgService.registerConfigFactory(factory);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700149 cfgService.addListener(cfgListener);
andreaeb70a942015-10-16 21:34:46 -0700150 controller.addDeviceListener(innerNodeListener);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700151 deviceService.addListener(deviceListener);
Andrea Campanella7d8449b2016-03-02 10:16:42 -0800152 executor.execute(NetconfDeviceProvider.this::connectDevices);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700153 localNodeId = clusterService.getLocalNode().id();
Thomas Vachuskad6811712015-04-29 21:37:04 -0700154 log.info("Started");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530155 }
156
andreaeb70a942015-10-16 21:34:46 -0700157
Sanjay Se8dcfee2015-04-23 10:07:08 +0530158 @Deactivate
andreaeb70a942015-10-16 21:34:46 -0700159 public void deactivate() {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700160 deviceService.removeListener(deviceListener);
161 active = false;
162 controller.getNetconfDevices().forEach(id -> {
163 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(id.toString()));
164 controller.disconnectDevice(id, true);
165 });
Andrea Campanella86294db2016-03-07 11:42:49 -0800166 controller.removeDeviceListener(innerNodeListener);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700167 deviceService.removeListener(deviceListener);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530168 providerRegistry.unregister(this);
169 providerService = null;
andreaeb70a942015-10-16 21:34:46 -0700170 cfgService.unregisterConfigFactory(factory);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700171 executor.shutdown();
Sanjay Seb5eebb2015-04-24 15:44:50 +0530172 log.info("Stopped");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530173 }
174
andreaeb70a942015-10-16 21:34:46 -0700175 public NetconfDeviceProvider() {
Andrea Campanella101417d2015-12-11 17:58:07 -0800176 super(new ProviderId(SCHEME_NAME, DEVICE_PROVIDER_PACKAGE));
Sanjay Se8dcfee2015-04-23 10:07:08 +0530177 }
178
179 @Override
180 public void triggerProbe(DeviceId deviceId) {
andreaeb70a942015-10-16 21:34:46 -0700181 // TODO: This will be implemented later.
182 log.info("Triggering probe on device {}", deviceId);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530183 }
184
185 @Override
186 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700187 if (active) {
188 switch (newRole) {
189 case MASTER:
190 initiateConnection(deviceId, newRole);
191 log.debug("Accepting mastership role change to {} for device {}", newRole, deviceId);
192 break;
193 case STANDBY:
194 controller.disconnectDevice(deviceId, false);
195 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.STANDBY);
196 //else no-op
197 break;
198 case NONE:
199 controller.disconnectDevice(deviceId, false);
200 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
201 break;
202 default:
203 log.error("Unimplemented Mastership state : {}", newRole);
204
205 }
206 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530207 }
208
209 @Override
210 public boolean isReachable(DeviceId deviceId) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700211 //FIXME this is a workaround util device state is shared
212 // between controller instances.
213 Device device = deviceService.getDevice(deviceId);
214 String ip;
215 int port;
216 Socket socket = null;
217 if (device != null) {
218 ip = device.annotations().value(IPADDRESS);
219 port = Integer.parseInt(device.annotations().value(PORT));
220 } else {
221 String[] info = deviceId.toString().split(":");
222 if (info.length == 3) {
223 ip = info[1];
224 port = Integer.parseInt(info[2]);
225 } else {
226 ip = Arrays.asList(info).stream().filter(el -> !el.equals(info[0])
227 && !el.equals(info[info.length - 1]))
228 .reduce((t, u) -> t + ":" + u)
229 .get();
230 log.debug("ip v6 {}", ip);
231 port = Integer.parseInt(info[info.length - 1]);
232 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530233 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700234 //test connection to device opening a socket to it.
235 try {
236 socket = new Socket(ip, port);
237 log.debug("rechability of {}, {}, {}", deviceId, socket.isConnected() && !socket.isClosed());
238 return socket.isConnected() && !socket.isClosed();
239 } catch (IOException e) {
240 log.info("Device {} is not reachable", deviceId);
241 return false;
242 } finally {
243 if (socket != null) {
244 try {
245 socket.close();
246 } catch (IOException e) {
247 log.debug("Test Socket failed {} ", deviceId);
248 return false;
249 }
250 }
251 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530252 }
253
Saurav Dasa2d37502016-03-25 17:50:40 -0700254 @Override
255 public void changePortState(DeviceId deviceId, PortNumber portNumber,
256 boolean enable) {
257 // TODO if required
258 }
259
andreaeb70a942015-10-16 21:34:46 -0700260 private class InnerNetconfDeviceListener implements NetconfDeviceListener {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530261
Andrea Campanella101417d2015-12-11 17:58:07 -0800262
andreaeb70a942015-10-16 21:34:46 -0700263 @Override
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700264 public void deviceAdded(DeviceId deviceId) {
265 //no-op
266 log.debug("Netconf device {} added to Netconf subController", deviceId);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530267 }
268
269 @Override
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700270 public void deviceRemoved(DeviceId deviceId) {
271 Preconditions.checkNotNull(deviceId, ISNULL);
272 log.debug("Netconf device {} removed from Netconf subController", deviceId);
andreaeb70a942015-10-16 21:34:46 -0700273 providerService.deviceDisconnected(deviceId);
andreaeb70a942015-10-16 21:34:46 -0700274 }
275 }
276
andreaeb70a942015-10-16 21:34:46 -0700277 private void connectDevices() {
278 NetconfProviderConfig cfg = cfgService.getConfig(appId, NetconfProviderConfig.class);
279 if (cfg != null) {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530280 try {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700281 cfg.getDevicesAddresses().stream().forEach(addr -> {
282 DeviceId deviceId = getDeviceId(addr.ip().toString(), addr.port());
283 Preconditions.checkNotNull(deviceId, ISNULL);
284 //Netconf configuration object
285 ChassisId cid = new ChassisId();
286 String ipAddress = addr.ip().toString();
287 SparseAnnotations annotations = DefaultAnnotations.builder()
288 .set(IPADDRESS, ipAddress)
289 .set(PORT, String.valueOf(addr.port()))
290 .set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase())
291 .build();
292 DeviceDescription deviceDescription = new DefaultDeviceDescription(
293 deviceId.uri(),
294 Device.Type.SWITCH,
295 UNKNOWN, UNKNOWN,
296 UNKNOWN, UNKNOWN,
297 cid,
298 annotations);
299 deviceKeyAdminService.addKey(
300 DeviceKey.createDeviceKeyUsingUsernamePassword(
301 DeviceKeyId.deviceKeyId(deviceId.toString()),
302 null, addr.name(), addr.password()));
303 providerService.deviceConnected(deviceId, deviceDescription);
Aaron Kruglikov17b4c852016-01-15 16:37:04 -0800304
Andrea Campanella087ceb92015-12-07 09:58:34 -0800305
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700306 });
andreaeb70a942015-10-16 21:34:46 -0700307 } catch (ConfigException e) {
308 log.error("Cannot read config error " + e);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530309 }
310 }
andreaeb70a942015-10-16 21:34:46 -0700311 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530312
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700313 private void initiateConnection(DeviceId deviceId, MastershipRole newRole) {
314 try {
315 if (isReachable(deviceId)) {
316 controller.connectDevice(deviceId);
317 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.MASTER);
318 } else {
319 return;
320 }
321 } catch (Exception e) {
322 if (deviceService.getDevice(deviceId) != null) {
323 providerService.deviceDisconnected(deviceId);
324 }
325 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(deviceId.toString()));
326 throw new RuntimeException(new NetconfException(
327 "Can't connect to NETCONF " + "device on " + deviceId + ":" + deviceId, e));
328
329 }
330 }
331
332 private void discoverPorts(DeviceId deviceId) {
333 Device device = deviceService.getDevice(deviceId);
334 if (device.is(PortDiscovery.class)) {
335 PortDiscovery portConfig = device.as(PortDiscovery.class);
336 providerService.updatePorts(deviceId,
337 portConfig.getPorts());
338 } else {
339 log.warn("No portGetter behaviour for device {}", deviceId);
340 }
341 }
342
343 /**
344 * Return the DeviceId about the device containing the URI.
345 *
346 * @return DeviceId
347 */
348 public DeviceId getDeviceId(String ip, int port) {
349 try {
350 return DeviceId.deviceId(new URI(NETCONF, ip + ":" + port, null));
351 } catch (URISyntaxException e) {
352 throw new IllegalArgumentException("Unable to build deviceID for device "
353 + ip + ":" + port, e);
354 }
355 }
356
357 /**
358 * Listener for configuration events.
359 */
andreaeb70a942015-10-16 21:34:46 -0700360 private class InternalNetworkConfigListener implements NetworkConfigListener {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530361
andreaeb70a942015-10-16 21:34:46 -0700362
363 @Override
364 public void event(NetworkConfigEvent event) {
Andrea Campanella90f044f2016-03-02 09:14:57 -0800365 executor.execute(NetconfDeviceProvider.this::connectDevices);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530366 }
367
andreaeb70a942015-10-16 21:34:46 -0700368 @Override
369 public boolean isRelevant(NetworkConfigEvent event) {
andreaeb70a942015-10-16 21:34:46 -0700370 return event.configClass().equals(NetconfProviderConfig.class) &&
371 (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
372 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530373 }
374 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700375
376 /**
377 * Listener for core device events.
378 */
379 private class InternalDeviceListener implements DeviceListener {
380 @Override
381 public void event(DeviceEvent event) {
382 if ((event.type() == DeviceEvent.Type.DEVICE_ADDED)) {
383 executor.execute(() -> discoverPorts(event.subject().id()));
384 } else if ((event.type() == DeviceEvent.Type.DEVICE_REMOVED)) {
385 log.debug("removing device {}", event.subject().id());
386 deviceService.getDevice(event.subject().id()).annotations().keys();
387 controller.disconnectDevice(event.subject().id(), true);
388 }
389 }
390
391 @Override
392 public boolean isRelevant(DeviceEvent event) {
393 if (mastershipService.getMasterFor(event.subject().id()) == null) {
394 return true;
395 }
396 return event.subject().annotations().value(AnnotationKeys.PROTOCOL)
397 .equals(SCHEME_NAME.toUpperCase()) &&
398 mastershipService.getMasterFor(event.subject().id()).equals(localNodeId);
399 }
400 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530401}