blob: 5fb6ead6e8199af3289138db36f36478708131f1 [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 */
16package org.onosproject.provider.netconf.device.impl;
17
18import static com.google.common.base.Strings.isNullOrEmpty;
19import static org.onlab.util.Tools.delay;
20import static org.onlab.util.Tools.get;
21import static org.onlab.util.Tools.groupedThreads;
22import static org.slf4j.LoggerFactory.getLogger;
23
24import java.net.URI;
25import java.net.URISyntaxException;
26import java.util.Dictionary;
27import java.util.Map;
28import java.util.Map.Entry;
29import java.util.concurrent.ConcurrentHashMap;
30import java.util.concurrent.ExecutorService;
31import java.util.concurrent.Executors;
32import java.util.concurrent.TimeUnit;
33
34import org.apache.felix.scr.annotations.Activate;
35import org.apache.felix.scr.annotations.Component;
36import org.apache.felix.scr.annotations.Deactivate;
37import org.apache.felix.scr.annotations.Modified;
38import org.apache.felix.scr.annotations.Property;
39import org.apache.felix.scr.annotations.Reference;
40import org.apache.felix.scr.annotations.ReferenceCardinality;
41import org.onlab.packet.ChassisId;
Thomas Vachuskad6811712015-04-29 21:37:04 -070042import org.onosproject.cfg.ComponentConfigService;
Sanjay Se8dcfee2015-04-23 10:07:08 +053043import org.onosproject.cluster.ClusterService;
44import org.onosproject.net.Device;
45import org.onosproject.net.DeviceId;
46import org.onosproject.net.MastershipRole;
47import org.onosproject.net.device.DefaultDeviceDescription;
48import org.onosproject.net.device.DeviceDescription;
49import org.onosproject.net.device.DeviceProvider;
50import org.onosproject.net.device.DeviceProviderRegistry;
51import org.onosproject.net.device.DeviceProviderService;
52import org.onosproject.net.device.DeviceService;
53import org.onosproject.net.provider.AbstractProvider;
54import org.onosproject.net.provider.ProviderId;
55import org.onosproject.provider.netconf.device.impl.NetconfDevice.DeviceState;
56import org.osgi.service.component.ComponentContext;
57import org.slf4j.Logger;
58
59/**
60 * Provider which will try to fetch the details of NETCONF devices from the core
61 * and run a capability discovery on each of the device.
62 */
63@Component(immediate = true)
64public class NetconfDeviceProvider extends AbstractProvider
65 implements DeviceProvider {
66
Sanjay Seb5eebb2015-04-24 15:44:50 +053067 private final Logger log = getLogger(NetconfDeviceProvider.class);
Sanjay Se8dcfee2015-04-23 10:07:08 +053068
69 private Map<DeviceId, NetconfDevice> netconfDeviceMap = new ConcurrentHashMap<DeviceId, NetconfDevice>();
70
71 private DeviceProviderService providerService;
72
73 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
74 protected DeviceProviderRegistry providerRegistry;
75
76 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
77 protected DeviceService deviceService;
78
79 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
80 protected ClusterService clusterService;
81
Thomas Vachuskad6811712015-04-29 21:37:04 -070082 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
83 protected ComponentConfigService cfgService;
84
Sanjay Se8dcfee2015-04-23 10:07:08 +053085 private ExecutorService deviceBuilder = Executors
Thomas Vachuskad6811712015-04-29 21:37:04 -070086 .newFixedThreadPool(1, groupedThreads("onos/netconf", "device-creator"));
Sanjay Se8dcfee2015-04-23 10:07:08 +053087
88 // Delay between events in ms.
89 private static final int EVENTINTERVAL = 5;
90
91 private static final String SCHEME = "netconf";
92
93 @Property(name = "devConfigs", value = "", label = "Instance-specific configurations")
94 private String devConfigs = null;
95
Thomas Vachuskad6811712015-04-29 21:37:04 -070096 @Property(name = "devPasswords", value = "", label = "Instance-specific password")
Sanjay Se8dcfee2015-04-23 10:07:08 +053097 private String devPasswords = null;
98
99 /**
100 * Creates a provider with the supplier identifier.
101 */
102 public NetconfDeviceProvider() {
103 super(new ProviderId("netconf", "org.onosproject.provider.netconf"));
104 }
105
106 @Activate
107 public void activate(ComponentContext context) {
Thomas Vachuskad6811712015-04-29 21:37:04 -0700108 cfgService.registerProperties(getClass());
Sanjay Se8dcfee2015-04-23 10:07:08 +0530109 providerService = providerRegistry.register(this);
110 modified(context);
Thomas Vachuskad6811712015-04-29 21:37:04 -0700111 log.info("Started");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530112 }
113
114 @Deactivate
115 public void deactivate(ComponentContext context) {
Thomas Vachuskad6811712015-04-29 21:37:04 -0700116 cfgService.unregisterProperties(getClass(), false);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530117 try {
118 for (Entry<DeviceId, NetconfDevice> deviceEntry : netconfDeviceMap
119 .entrySet()) {
120 deviceBuilder.submit(new DeviceCreator(deviceEntry.getValue(),
121 false));
122 }
123 deviceBuilder.awaitTermination(1000, TimeUnit.MILLISECONDS);
124 } catch (InterruptedException e) {
Sanjay Seb5eebb2015-04-24 15:44:50 +0530125 log.error("Device builder did not terminate");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530126 }
127 deviceBuilder.shutdownNow();
128 netconfDeviceMap.clear();
129 providerRegistry.unregister(this);
130 providerService = null;
Sanjay Seb5eebb2015-04-24 15:44:50 +0530131 log.info("Stopped");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530132 }
133
134 @Modified
135 public void modified(ComponentContext context) {
136 if (context == null) {
Sanjay Seb5eebb2015-04-24 15:44:50 +0530137 log.info("No configuration file");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530138 return;
139 }
140 Dictionary<?, ?> properties = context.getProperties();
141 String deviceCfgValue = get(properties, "devConfigs");
Thomas Vachuskad6811712015-04-29 21:37:04 -0700142 log.info("Settings: devConfigs={}", deviceCfgValue);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530143 if (!isNullOrEmpty(deviceCfgValue)) {
144 addOrRemoveDevicesConfig(deviceCfgValue);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530145 }
146 }
147
148 private void addOrRemoveDevicesConfig(String deviceConfig) {
149 for (String deviceEntry : deviceConfig.split(",")) {
150 NetconfDevice device = processDeviceEntry(deviceEntry);
151 if (device != null) {
Thomas Vachuskad6811712015-04-29 21:37:04 -0700152 log.info("Device Detail: username: {}, host={}, port={}, state={}",
153 device.getUsername(), device.getSshHost(),
154 device.getSshPort(), device.getDeviceState().name());
Sanjay Se8dcfee2015-04-23 10:07:08 +0530155 if (device.isActive()) {
156 deviceBuilder.submit(new DeviceCreator(device, true));
157 } else {
158 deviceBuilder.submit(new DeviceCreator(device, false));
159 }
160 }
161 }
162 }
163
164 private NetconfDevice processDeviceEntry(String deviceEntry) {
165 if (deviceEntry == null) {
Sanjay Seb5eebb2015-04-24 15:44:50 +0530166 log.info("No content for Device Entry, so cannot proceed further.");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530167 return null;
168 }
Sanjay Seb5eebb2015-04-24 15:44:50 +0530169 log.info("Trying to convert Device Entry String: " + deviceEntry
170 + " to a Netconf Device Object");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530171 NetconfDevice device = null;
172 try {
173 String userInfo = deviceEntry.substring(0, deviceEntry
174 .lastIndexOf('@'));
175 String hostInfo = deviceEntry.substring(deviceEntry
176 .lastIndexOf('@') + 1);
177 String[] infoSplit = userInfo.split(":");
178 String username = infoSplit[0];
179 String password = infoSplit[1];
180 infoSplit = hostInfo.split(":");
181 String hostIp = infoSplit[0];
182 Integer hostPort;
183 try {
184 hostPort = Integer.parseInt(infoSplit[1]);
185 } catch (NumberFormatException nfe) {
Sanjay Seb5eebb2015-04-24 15:44:50 +0530186 log.error("Bad Configuration Data: Failed to parse host port number string: "
187 + infoSplit[1]);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530188 throw nfe;
189 }
190 String deviceState = infoSplit[2];
191 if (isNullOrEmpty(username) || isNullOrEmpty(password)
192 || isNullOrEmpty(hostIp) || hostPort == 0) {
Sanjay Seb5eebb2015-04-24 15:44:50 +0530193 log.warn("Bad Configuration Data: both user and device information parts of Configuration "
194 + deviceEntry + " should be non-nullable");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530195 } else {
196 device = new NetconfDevice(hostIp, hostPort, username, password);
197 if (!isNullOrEmpty(deviceState)) {
198 if (deviceState.toUpperCase().equals(DeviceState.ACTIVE
199 .name())) {
200 device.setDeviceState(DeviceState.ACTIVE);
201 } else if (deviceState.toUpperCase()
202 .equals(DeviceState.INACTIVE.name())) {
Sanjay Seb5eebb2015-04-24 15:44:50 +0530203 device.setDeviceState(DeviceState.INACTIVE);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530204 } else {
Sanjay Seb5eebb2015-04-24 15:44:50 +0530205 log.warn("Device State Information can not be empty, so marking the state as INVALID");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530206 device.setDeviceState(DeviceState.INVALID);
207 }
208 } else {
Sanjay Seb5eebb2015-04-24 15:44:50 +0530209 log.warn("The device entry do not specify state information, so marking the state as INVALID");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530210 device.setDeviceState(DeviceState.INVALID);
211 }
212 }
213 } catch (ArrayIndexOutOfBoundsException aie) {
Sanjay Seb5eebb2015-04-24 15:44:50 +0530214 log.error("Error while reading config infromation from the config file: "
215 + "The user, host and device state infomation should be "
216 + "in the order 'userInfo@hostInfo:deviceState'"
217 + deviceEntry, aie);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530218 } catch (Exception e) {
Sanjay Seb5eebb2015-04-24 15:44:50 +0530219 log.error("Error while parsing config information for the device entry: "
220 + deviceEntry, e);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530221 }
222 return device;
223 }
224
225 @Override
226 public void triggerProbe(DeviceId deviceId) {
227 // TODO Auto-generated method stub
228 }
229
230 @Override
231 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
232
233 }
234
235 @Override
236 public boolean isReachable(DeviceId deviceId) {
237 NetconfDevice netconfDevice = netconfDeviceMap.get(deviceId);
238 if (netconfDevice == null) {
Sanjay Seb5eebb2015-04-24 15:44:50 +0530239 log.warn("BAD REQUEST: the requested device id: "
240 + deviceId.toString()
241 + " is not associated to any NETCONF Device");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530242 return false;
243 }
244 return netconfDevice.isReachable();
245 }
246
247 /**
248 * This class is intended to add or remove Configured Netconf Devices.
249 * Functionality relies on 'createFlag' and 'NetconfDevice' content. The
250 * functionality runs as a thread and dependening on the 'createFlag' value
251 * it will create or remove Device entry from the core.
252 */
253 private class DeviceCreator implements Runnable {
254
255 private NetconfDevice device;
256 private boolean createFlag;
257
258 public DeviceCreator(NetconfDevice device, boolean createFlag) {
259 this.device = device;
260 this.createFlag = createFlag;
261 }
262
263 @Override
264 public void run() {
Sanjay Seb5eebb2015-04-24 15:44:50 +0530265 if (createFlag) {
266 log.info("Trying to create Device Info on ONOS core");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530267 advertiseDevices();
268 } else {
Sanjay Seb5eebb2015-04-24 15:44:50 +0530269 log.info("Trying to remove Device Info on ONOS core");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530270 removeDevices();
271 }
272 }
273
274 /**
275 * For each Netconf Device, remove the entry from the device store.
276 */
277 private void removeDevices() {
Sanjay Seb5eebb2015-04-24 15:44:50 +0530278 if (device == null) {
279 log.warn("The Request Netconf Device is null, cannot proceed further");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530280 return;
281 }
282 try {
283 DeviceId did = getDeviceId();
Sanjay Seb5eebb2015-04-24 15:44:50 +0530284 if (!netconfDeviceMap.containsKey(did)) {
285 log.error("BAD Request: 'Currently device is not discovered, "
286 + "so cannot remove/disconnect the device: "
287 + device.deviceInfo() + "'");
288 return;
289 }
Sanjay Se8dcfee2015-04-23 10:07:08 +0530290 providerService.deviceDisconnected(did);
291 device.disconnect();
Sanjay Seb5eebb2015-04-24 15:44:50 +0530292 netconfDeviceMap.remove(did);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530293 delay(EVENTINTERVAL);
294 } catch (URISyntaxException uriSyntaxExcpetion) {
Sanjay Seb5eebb2015-04-24 15:44:50 +0530295 log.error("Syntax Error while creating URI for the device: "
296 + device.deviceInfo()
297 + " couldn't remove the device from the store",
298 uriSyntaxExcpetion);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530299 }
300 }
301
302 /**
303 * Initialize Netconf Device object, and notify core saying device
304 * connected.
305 */
306 private void advertiseDevices() {
307 try {
308 if (device == null) {
Sanjay Seb5eebb2015-04-24 15:44:50 +0530309 log.warn("The Request Netconf Device is null, cannot proceed further");
Sanjay Se8dcfee2015-04-23 10:07:08 +0530310 return;
311 }
312 device.init();
313 DeviceId did = getDeviceId();
314 ChassisId cid = new ChassisId();
315 DeviceDescription desc = new DefaultDeviceDescription(
316 did.uri(),
317 Device.Type.OTHER,
318 "", "",
319 "", "",
320 cid);
Sanjay Seb5eebb2015-04-24 15:44:50 +0530321 log.info("Persisting Device" + did.uri().toString());
Sanjay Se8dcfee2015-04-23 10:07:08 +0530322
323 netconfDeviceMap.put(did, device);
324 providerService.deviceConnected(did, desc);
Sanjay Seb5eebb2015-04-24 15:44:50 +0530325 log.info("Done with Device Info Creation on ONOS core. Device Info: "
326 + device.deviceInfo() + " " + did.uri().toString());
Sanjay Se8dcfee2015-04-23 10:07:08 +0530327 delay(EVENTINTERVAL);
328 } catch (URISyntaxException e) {
Sanjay Seb5eebb2015-04-24 15:44:50 +0530329 log.error("Syntax Error while creating URI for the device: "
330 + device.deviceInfo()
331 + " couldn't persist the device onto the store", e);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530332 } catch (Exception e) {
Sanjay Seb5eebb2015-04-24 15:44:50 +0530333 log.error("Error while initializing session for the device: "
334 + device.deviceInfo(), e);
Sanjay Se8dcfee2015-04-23 10:07:08 +0530335 }
336 }
337
338 /**
339 * This will build a device id for the device.
340 */
341 private DeviceId getDeviceId() throws URISyntaxException {
342 String additionalSSP = new StringBuilder(device.getUsername())
343 .append("@").append(device.getSshHost()).append(":")
344 .append(device.getSshPort()).toString();
345 DeviceId did = DeviceId.deviceId(new URI(SCHEME, additionalSSP,
346 null));
347 return did;
348 }
349 }
350}