blob: 6025e073d250251a9a6b3b1b09ac83cbb972ebad [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;
andreaeb70a942015-10-16 21:34:46 -070037import org.onosproject.net.SparseAnnotations;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -080038import org.onosproject.net.behaviour.PortDiscovery;
andreaeb70a942015-10-16 21:34:46 -070039import org.onosproject.net.config.ConfigFactory;
40import org.onosproject.net.config.NetworkConfigEvent;
41import org.onosproject.net.config.NetworkConfigListener;
42import org.onosproject.net.config.NetworkConfigRegistry;
Sanjay Se8dcfee2015-04-23 10:07:08 +053043import org.onosproject.net.device.DefaultDeviceDescription;
44import org.onosproject.net.device.DeviceDescription;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070045import org.onosproject.net.device.DeviceEvent;
46import org.onosproject.net.device.DeviceListener;
Sanjay Se8dcfee2015-04-23 10:07:08 +053047import org.onosproject.net.device.DeviceProvider;
48import org.onosproject.net.device.DeviceProviderRegistry;
49import org.onosproject.net.device.DeviceProviderService;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -080050import org.onosproject.net.device.DeviceService;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070051import org.onosproject.net.key.DeviceKey;
52import org.onosproject.net.key.DeviceKeyAdminService;
53import org.onosproject.net.key.DeviceKeyId;
Sanjay Se8dcfee2015-04-23 10:07:08 +053054import org.onosproject.net.provider.AbstractProvider;
55import org.onosproject.net.provider.ProviderId;
andreaeb70a942015-10-16 21:34:46 -070056import org.onosproject.netconf.NetconfController;
andreaeb70a942015-10-16 21:34:46 -070057import org.onosproject.netconf.NetconfDeviceListener;
Andrea Campanella8b1cb672016-01-25 13:58:58 -080058import org.onosproject.netconf.NetconfException;
Sanjay Se8dcfee2015-04-23 10:07:08 +053059import org.slf4j.Logger;
60
Andrea Campanella087ceb92015-12-07 09:58:34 -080061import java.io.IOException;
Andrea Campanella7e6200a2016-03-21 09:48:40 -070062import java.net.Socket;
63import java.net.URI;
64import java.net.URISyntaxException;
65import java.util.Arrays;
Andrea Campanella5c999e22016-03-01 15:12:53 -080066import java.util.concurrent.ExecutorService;
67import java.util.concurrent.Executors;
andreaeb70a942015-10-16 21:34:46 -070068
Andrea Campanella5c999e22016-03-01 15:12:53 -080069import static org.onlab.util.Tools.groupedThreads;
andreaeb70a942015-10-16 21:34:46 -070070import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
71import static org.slf4j.LoggerFactory.getLogger;
72
Sanjay Se8dcfee2015-04-23 10:07:08 +053073/**
andreaeb70a942015-10-16 21:34:46 -070074 * Provider which uses an NETCONF controller to detect device.
Sanjay Se8dcfee2015-04-23 10:07:08 +053075 */
76@Component(immediate = true)
77public class NetconfDeviceProvider extends AbstractProvider
78 implements DeviceProvider {
Andrea Campanella7e6200a2016-03-21 09:48:40 -070079
80 public static final String ACTIVE = "active";
andreaeb70a942015-10-16 21:34:46 -070081 private final Logger log = getLogger(getClass());
Sanjay Se8dcfee2015-04-23 10:07:08 +053082
83 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
84 protected DeviceProviderRegistry providerRegistry;
85
andreaeb70a942015-10-16 21:34:46 -070086 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella101417d2015-12-11 17:58:07 -080087 protected NetconfController controller;
Sanjay Se8dcfee2015-04-23 10:07:08 +053088
89 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
andreaeb70a942015-10-16 21:34:46 -070090 protected NetworkConfigRegistry cfgService;
Sanjay Se8dcfee2015-04-23 10:07:08 +053091
Thomas Vachuskad6811712015-04-29 21:37:04 -070092 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
andreaeb70a942015-10-16 21:34:46 -070093 protected CoreService coreService;
Thomas Vachuskad6811712015-04-29 21:37:04 -070094
Aaron Kruglikov17b4c852016-01-15 16:37:04 -080095 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -070096 protected DeviceService deviceService;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -080097
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanella7e6200a2016-03-21 09:48:40 -070099 protected DeviceKeyAdminService deviceKeyAdminService;
100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
102 protected MastershipService mastershipService;
103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 protected ClusterService clusterService;
Aaron Kruglikov17b4c852016-01-15 16:37:04 -0800106
Andrea Campanella101417d2015-12-11 17:58:07 -0800107 private static final String APP_NAME = "org.onosproject.netconf";
108 private static final String SCHEME_NAME = "netconf";
109 private static final String DEVICE_PROVIDER_PACKAGE = "org.onosproject.netconf.provider.device";
110 private static final String UNKNOWN = "unknown";
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700111 protected static final String ISNULL = "NetconfDeviceInfo is null";
112 private static final String IPADDRESS = "ipaddress";
113 private static final String NETCONF = "netconf";
114 private static final String PORT = "port";
115 //FIXME eventually a property
116 private static final int ISREACHABLE_TIMEOUT = 2000;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530117
Andrea Campanella5c999e22016-03-01 15:12:53 -0800118 private final ExecutorService executor =
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700119 Executors.newFixedThreadPool(5, groupedThreads("onos/netconfdeviceprovider",
120 "device-installer-%d", log));
Andrea Campanella5c999e22016-03-01 15:12:53 -0800121
andreaeb70a942015-10-16 21:34:46 -0700122 private DeviceProviderService providerService;
123 private NetconfDeviceListener innerNodeListener = new InnerNetconfDeviceListener();
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700124 private InternalDeviceListener deviceListener = new InternalDeviceListener();
125 private NodeId localNodeId;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530126
andreaeb70a942015-10-16 21:34:46 -0700127 private final ConfigFactory factory =
128 new ConfigFactory<ApplicationId, NetconfProviderConfig>(APP_SUBJECT_FACTORY,
129 NetconfProviderConfig.class,
130 "devices",
131 true) {
132 @Override
133 public NetconfProviderConfig createConfig() {
134 return new NetconfProviderConfig();
135 }
136 };
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700137 private final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
andreaeb70a942015-10-16 21:34:46 -0700138 private ApplicationId appId;
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700139 private boolean active;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530140
Sanjay Se8dcfee2015-04-23 10:07:08 +0530141
142 @Activate
andreaeb70a942015-10-16 21:34:46 -0700143 public void activate() {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700144 active = true;
Sanjay Se8dcfee2015-04-23 10:07:08 +0530145 providerService = providerRegistry.register(this);
Andrea Campanella101417d2015-12-11 17:58:07 -0800146 appId = coreService.registerApplication(APP_NAME);
andreaeb70a942015-10-16 21:34:46 -0700147 cfgService.registerConfigFactory(factory);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700148 cfgService.addListener(cfgListener);
andreaeb70a942015-10-16 21:34:46 -0700149 controller.addDeviceListener(innerNodeListener);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700150 deviceService.addListener(deviceListener);
Andrea Campanella7d8449b2016-03-02 10:16:42 -0800151 executor.execute(NetconfDeviceProvider.this::connectDevices);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700152 localNodeId = clusterService.getLocalNode().id();
Thomas Vachuskad6811712015-04-29 21:37:04 -0700153 log.info("Started");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530154 }
155
andreaeb70a942015-10-16 21:34:46 -0700156
Sanjay Se8dcfee2015-04-23 10:07:08 +0530157 @Deactivate
andreaeb70a942015-10-16 21:34:46 -0700158 public void deactivate() {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700159 deviceService.removeListener(deviceListener);
160 active = false;
161 controller.getNetconfDevices().forEach(id -> {
162 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(id.toString()));
163 controller.disconnectDevice(id, true);
164 });
Andrea Campanella86294db2016-03-07 11:42:49 -0800165 controller.removeDeviceListener(innerNodeListener);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700166 deviceService.removeListener(deviceListener);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530167 providerRegistry.unregister(this);
168 providerService = null;
andreaeb70a942015-10-16 21:34:46 -0700169 cfgService.unregisterConfigFactory(factory);
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700170 executor.shutdown();
Sanjay Seb5eebb2015-04-24 15:44:50 +0530171 log.info("Stopped");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530172 }
173
andreaeb70a942015-10-16 21:34:46 -0700174 public NetconfDeviceProvider() {
Andrea Campanella101417d2015-12-11 17:58:07 -0800175 super(new ProviderId(SCHEME_NAME, DEVICE_PROVIDER_PACKAGE));
Sanjay Se8dcfee2015-04-23 10:07:08 +0530176 }
177
178 @Override
179 public void triggerProbe(DeviceId deviceId) {
andreaeb70a942015-10-16 21:34:46 -0700180 // TODO: This will be implemented later.
181 log.info("Triggering probe on device {}", deviceId);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530182 }
183
184 @Override
185 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700186 if (active) {
187 switch (newRole) {
188 case MASTER:
189 initiateConnection(deviceId, newRole);
190 log.debug("Accepting mastership role change to {} for device {}", newRole, deviceId);
191 break;
192 case STANDBY:
193 controller.disconnectDevice(deviceId, false);
194 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.STANDBY);
195 //else no-op
196 break;
197 case NONE:
198 controller.disconnectDevice(deviceId, false);
199 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
200 break;
201 default:
202 log.error("Unimplemented Mastership state : {}", newRole);
203
204 }
205 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530206 }
207
208 @Override
209 public boolean isReachable(DeviceId deviceId) {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700210 //FIXME this is a workaround util device state is shared
211 // between controller instances.
212 Device device = deviceService.getDevice(deviceId);
213 String ip;
214 int port;
215 Socket socket = null;
216 if (device != null) {
217 ip = device.annotations().value(IPADDRESS);
218 port = Integer.parseInt(device.annotations().value(PORT));
219 } else {
220 String[] info = deviceId.toString().split(":");
221 if (info.length == 3) {
222 ip = info[1];
223 port = Integer.parseInt(info[2]);
224 } else {
225 ip = Arrays.asList(info).stream().filter(el -> !el.equals(info[0])
226 && !el.equals(info[info.length - 1]))
227 .reduce((t, u) -> t + ":" + u)
228 .get();
229 log.debug("ip v6 {}", ip);
230 port = Integer.parseInt(info[info.length - 1]);
231 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530232 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700233 //test connection to device opening a socket to it.
234 try {
235 socket = new Socket(ip, port);
236 log.debug("rechability of {}, {}, {}", deviceId, socket.isConnected() && !socket.isClosed());
237 return socket.isConnected() && !socket.isClosed();
238 } catch (IOException e) {
239 log.info("Device {} is not reachable", deviceId);
240 return false;
241 } finally {
242 if (socket != null) {
243 try {
244 socket.close();
245 } catch (IOException e) {
246 log.debug("Test Socket failed {} ", deviceId);
247 return false;
248 }
249 }
250 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530251 }
252
andreaeb70a942015-10-16 21:34:46 -0700253 private class InnerNetconfDeviceListener implements NetconfDeviceListener {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530254
Andrea Campanella101417d2015-12-11 17:58:07 -0800255
andreaeb70a942015-10-16 21:34:46 -0700256 @Override
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700257 public void deviceAdded(DeviceId deviceId) {
258 //no-op
259 log.debug("Netconf device {} added to Netconf subController", deviceId);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530260 }
261
262 @Override
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700263 public void deviceRemoved(DeviceId deviceId) {
264 Preconditions.checkNotNull(deviceId, ISNULL);
265 log.debug("Netconf device {} removed from Netconf subController", deviceId);
andreaeb70a942015-10-16 21:34:46 -0700266 providerService.deviceDisconnected(deviceId);
andreaeb70a942015-10-16 21:34:46 -0700267 }
268 }
269
andreaeb70a942015-10-16 21:34:46 -0700270 private void connectDevices() {
271 NetconfProviderConfig cfg = cfgService.getConfig(appId, NetconfProviderConfig.class);
272 if (cfg != null) {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530273 try {
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700274 cfg.getDevicesAddresses().stream().forEach(addr -> {
275 DeviceId deviceId = getDeviceId(addr.ip().toString(), addr.port());
276 Preconditions.checkNotNull(deviceId, ISNULL);
277 //Netconf configuration object
278 ChassisId cid = new ChassisId();
279 String ipAddress = addr.ip().toString();
280 SparseAnnotations annotations = DefaultAnnotations.builder()
281 .set(IPADDRESS, ipAddress)
282 .set(PORT, String.valueOf(addr.port()))
283 .set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase())
284 .build();
285 DeviceDescription deviceDescription = new DefaultDeviceDescription(
286 deviceId.uri(),
287 Device.Type.SWITCH,
288 UNKNOWN, UNKNOWN,
289 UNKNOWN, UNKNOWN,
290 cid,
291 annotations);
292 deviceKeyAdminService.addKey(
293 DeviceKey.createDeviceKeyUsingUsernamePassword(
294 DeviceKeyId.deviceKeyId(deviceId.toString()),
295 null, addr.name(), addr.password()));
296 providerService.deviceConnected(deviceId, deviceDescription);
Aaron Kruglikov17b4c852016-01-15 16:37:04 -0800297
Andrea Campanella087ceb92015-12-07 09:58:34 -0800298
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700299 });
andreaeb70a942015-10-16 21:34:46 -0700300 } catch (ConfigException e) {
301 log.error("Cannot read config error " + e);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530302 }
303 }
andreaeb70a942015-10-16 21:34:46 -0700304 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530305
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700306 private void initiateConnection(DeviceId deviceId, MastershipRole newRole) {
307 try {
308 if (isReachable(deviceId)) {
309 controller.connectDevice(deviceId);
310 providerService.receivedRoleReply(deviceId, newRole, MastershipRole.MASTER);
311 } else {
312 return;
313 }
314 } catch (Exception e) {
315 if (deviceService.getDevice(deviceId) != null) {
316 providerService.deviceDisconnected(deviceId);
317 }
318 deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(deviceId.toString()));
319 throw new RuntimeException(new NetconfException(
320 "Can't connect to NETCONF " + "device on " + deviceId + ":" + deviceId, e));
321
322 }
323 }
324
325 private void discoverPorts(DeviceId deviceId) {
326 Device device = deviceService.getDevice(deviceId);
327 if (device.is(PortDiscovery.class)) {
328 PortDiscovery portConfig = device.as(PortDiscovery.class);
329 providerService.updatePorts(deviceId,
330 portConfig.getPorts());
331 } else {
332 log.warn("No portGetter behaviour for device {}", deviceId);
333 }
334 }
335
336 /**
337 * Return the DeviceId about the device containing the URI.
338 *
339 * @return DeviceId
340 */
341 public DeviceId getDeviceId(String ip, int port) {
342 try {
343 return DeviceId.deviceId(new URI(NETCONF, ip + ":" + port, null));
344 } catch (URISyntaxException e) {
345 throw new IllegalArgumentException("Unable to build deviceID for device "
346 + ip + ":" + port, e);
347 }
348 }
349
350 /**
351 * Listener for configuration events.
352 */
andreaeb70a942015-10-16 21:34:46 -0700353 private class InternalNetworkConfigListener implements NetworkConfigListener {
Sanjay Se8dcfee2015-04-23 10:07:08 +0530354
andreaeb70a942015-10-16 21:34:46 -0700355
356 @Override
357 public void event(NetworkConfigEvent event) {
Andrea Campanella90f044f2016-03-02 09:14:57 -0800358 executor.execute(NetconfDeviceProvider.this::connectDevices);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530359 }
360
andreaeb70a942015-10-16 21:34:46 -0700361 @Override
362 public boolean isRelevant(NetworkConfigEvent event) {
andreaeb70a942015-10-16 21:34:46 -0700363 return event.configClass().equals(NetconfProviderConfig.class) &&
364 (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
365 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530366 }
367 }
Andrea Campanella7e6200a2016-03-21 09:48:40 -0700368
369 /**
370 * Listener for core device events.
371 */
372 private class InternalDeviceListener implements DeviceListener {
373 @Override
374 public void event(DeviceEvent event) {
375 if ((event.type() == DeviceEvent.Type.DEVICE_ADDED)) {
376 executor.execute(() -> discoverPorts(event.subject().id()));
377 } else if ((event.type() == DeviceEvent.Type.DEVICE_REMOVED)) {
378 log.debug("removing device {}", event.subject().id());
379 deviceService.getDevice(event.subject().id()).annotations().keys();
380 controller.disconnectDevice(event.subject().id(), true);
381 }
382 }
383
384 @Override
385 public boolean isRelevant(DeviceEvent event) {
386 if (mastershipService.getMasterFor(event.subject().id()) == null) {
387 return true;
388 }
389 return event.subject().annotations().value(AnnotationKeys.PROTOCOL)
390 .equals(SCHEME_NAME.toUpperCase()) &&
391 mastershipService.getMasterFor(event.subject().id()).equals(localNodeId);
392 }
393 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530394}