blob: c32296c2dbce4e975a398b13d9a1f0f69855d2f9 [file] [log] [blame]
kmcpeakeb172d5f2015-12-10 11:30:43 +00001/*
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.snmp.device.impl;
17
18import com.btisystems.pronx.ems.core.snmp.DefaultSnmpConfigurationFactory;
19import com.btisystems.pronx.ems.core.snmp.ISnmpConfiguration;
20import com.btisystems.pronx.ems.core.snmp.ISnmpConfigurationFactory;
21import com.btisystems.pronx.ems.core.snmp.ISnmpSession;
22import com.btisystems.pronx.ems.core.snmp.ISnmpSessionFactory;
23import com.btisystems.pronx.ems.core.snmp.SnmpSessionFactory;
24import com.btisystems.pronx.ems.core.snmp.V2cSnmpConfiguration;
kmcpeakeb172d5f2015-12-10 11:30:43 +000025import org.apache.felix.scr.annotations.Activate;
26import org.apache.felix.scr.annotations.Component;
27import org.apache.felix.scr.annotations.Deactivate;
28import org.apache.felix.scr.annotations.Modified;
29import org.apache.felix.scr.annotations.Property;
30import org.apache.felix.scr.annotations.Reference;
31import org.apache.felix.scr.annotations.ReferenceCardinality;
32import org.onlab.packet.ChassisId;
33import org.onosproject.cfg.ComponentConfigService;
34import org.onosproject.cluster.ClusterService;
Marc De Leenheerc662d322016-02-18 16:05:10 -080035import org.onosproject.net.AnnotationKeys;
36import org.onosproject.net.DefaultAnnotations;
kmcpeakeb172d5f2015-12-10 11:30:43 +000037import org.onosproject.net.Device;
38import org.onosproject.net.DeviceId;
39import org.onosproject.net.MastershipRole;
Saurav Dasa2d37502016-03-25 17:50:40 -070040import org.onosproject.net.PortNumber;
Marc De Leenheerc662d322016-02-18 16:05:10 -080041import org.onosproject.net.SparseAnnotations;
42import org.onosproject.net.behaviour.PortDiscovery;
kmcpeakeb172d5f2015-12-10 11:30:43 +000043import org.onosproject.net.device.DefaultDeviceDescription;
44import org.onosproject.net.device.DeviceDescription;
45import org.onosproject.net.device.DeviceProvider;
46import org.onosproject.net.device.DeviceProviderRegistry;
47import org.onosproject.net.device.DeviceProviderService;
48import org.onosproject.net.device.DeviceService;
49import org.onosproject.net.provider.AbstractProvider;
50import org.onosproject.net.provider.ProviderId;
51import org.osgi.service.component.ComponentContext;
52import org.slf4j.Logger;
53
Jonathan Hart51539b82015-10-29 09:53:04 -070054import java.io.IOException;
55import java.net.URI;
56import java.net.URISyntaxException;
57import java.util.Dictionary;
58import java.util.HashMap;
59import java.util.Map;
60import java.util.concurrent.ConcurrentHashMap;
61import java.util.concurrent.ExecutorService;
62import java.util.concurrent.Executors;
63import java.util.concurrent.TimeUnit;
64
65import static com.google.common.base.Strings.isNullOrEmpty;
66import static org.onlab.util.Tools.delay;
67import static org.onlab.util.Tools.get;
68import static org.onlab.util.Tools.groupedThreads;
69import static org.slf4j.LoggerFactory.getLogger;
70
kmcpeakeb172d5f2015-12-10 11:30:43 +000071/**
72 * Provider which will try to fetch the details of SNMP devices from the core and run a capability discovery on each of
73 * the device.
74 */
75@Component(immediate = true)
76public class SnmpDeviceProvider extends AbstractProvider
77 implements DeviceProvider {
78
79 private final Logger log = getLogger(SnmpDeviceProvider.class);
80
81 private static final String UNKNOWN = "unknown";
82
83 protected Map<DeviceId, SnmpDevice> snmpDeviceMap = new ConcurrentHashMap<>();
84
85 private DeviceProviderService providerService;
86
87 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
88 protected DeviceProviderRegistry providerRegistry;
89
90 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
91 protected DeviceService deviceService;
92
93 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
94 protected ClusterService clusterService;
95
96 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
97 protected ComponentConfigService cfgService;
98
99 private final ExecutorService deviceBuilder = Executors
Andrea Campanella90f044f2016-03-02 09:14:57 -0800100 .newFixedThreadPool(1, groupedThreads("onos/snmp", "device-creator", log));
kmcpeakeb172d5f2015-12-10 11:30:43 +0000101
102 // Delay between events in ms.
103 private static final int EVENTINTERVAL = 5;
104
105 private static final String SCHEME = "snmp";
106
107 @Property(name = "devConfigs", value = "", label = "Instance-specific configurations")
108 private String devConfigs = null;
109
110 @Property(name = "devPasswords", value = "", label = "Instance-specific password")
111 private String devPasswords = null;
112
113 //TODO Could be replaced with a service lookup, and bundles per device variant.
114 Map<String, SnmpDeviceDescriptionProvider> providers = new HashMap<>();
115
116 private final ISnmpSessionFactory sessionFactory;
117
118 /**
119 * Creates a provider with the supplier identifier.
120 */
121 public SnmpDeviceProvider() {
122 super(new ProviderId("snmp", "org.onosproject.provider.device"));
123 sessionFactory = new SnmpSessionFactory(
124 new DefaultSnmpConfigurationFactory(new V2cSnmpConfiguration()));
Andrea Campanella3eca4a82016-02-10 17:35:14 -0800125 //TODO refactor, no hardcoding in provider, device information should be in drivers
kmcpeakeb172d5f2015-12-10 11:30:43 +0000126 providers.put("1.3.6.1.4.1.18070.2.2", new Bti7000DeviceDescriptionProvider());
127 providers.put("1.3.6.1.4.1.20408", new NetSnmpDeviceDescriptionProvider());
Marc De Leenheerc662d322016-02-18 16:05:10 -0800128 providers.put("1.3.6.1.4.562.73.6", new LumentumDeviceDescriptionProvider());
kmcpeakeb172d5f2015-12-10 11:30:43 +0000129 }
130
131 @Activate
132 public void activate(ComponentContext context) {
133 log.info("activating for snmp devices ...");
134 cfgService.registerProperties(getClass());
135 providerService = providerRegistry.register(this);
136 modified(context);
137 log.info("activated ok");
138 }
139
140 @Deactivate
141 public void deactivate(ComponentContext context) {
142
143 log.info("deactivating for snmp devices ...");
144
145 cfgService.unregisterProperties(getClass(), false);
146 try {
147 snmpDeviceMap
148 .entrySet().stream().forEach((deviceEntry) -> {
Andrea Campanella90f044f2016-03-02 09:14:57 -0800149 deviceBuilder.execute(new DeviceCreator(deviceEntry.getValue(), false));
kmcpeakeb172d5f2015-12-10 11:30:43 +0000150 });
151 deviceBuilder.awaitTermination(1000, TimeUnit.MILLISECONDS);
152 } catch (InterruptedException e) {
153 log.error("Device builder did not terminate");
154 }
155 deviceBuilder.shutdownNow();
156 snmpDeviceMap.clear();
157 providerRegistry.unregister(this);
158 providerService = null;
159 log.info("Stopped");
160 }
161
162 @Modified
163 public void modified(ComponentContext context) {
164 log.info("modified ...");
165
166 if (context == null) {
167 log.info("No configuration file");
168 return;
169 }
170 Dictionary<?, ?> properties = context.getProperties();
171
172 log.info("properties={}", context.getProperties());
173
174 String deviceCfgValue = get(properties, "devConfigs");
175 log.info("Settings: devConfigs={}", deviceCfgValue);
176 if (!isNullOrEmpty(deviceCfgValue)) {
177 addOrRemoveDevicesConfig(deviceCfgValue);
178 }
179 log.info("... modified");
180
181 }
182
183 private void addOrRemoveDevicesConfig(String deviceConfig) {
184 for (String deviceEntry : deviceConfig.split(",")) {
185 SnmpDevice device = processDeviceEntry(deviceEntry);
186 if (device != null) {
187 log.info("Device Detail:host={}, port={}, state={}",
188 new Object[]{device.getSnmpHost(),
189 device.getSnmpPort(),
190 device.getDeviceState().name()}
191 );
192 if (device.isActive()) {
Andrea Campanella90f044f2016-03-02 09:14:57 -0800193 deviceBuilder.execute(new DeviceCreator(device, true));
kmcpeakeb172d5f2015-12-10 11:30:43 +0000194 } else {
Andrea Campanella90f044f2016-03-02 09:14:57 -0800195 deviceBuilder.execute(new DeviceCreator(device, false));
kmcpeakeb172d5f2015-12-10 11:30:43 +0000196 }
197 }
198 }
199 }
200
201 private SnmpDevice processDeviceEntry(String deviceEntry) {
202 if (deviceEntry == null) {
203 log.info("No content for Device Entry, so cannot proceed further.");
204 return null;
205 }
206 log.info("Trying to convert {} to a SNMP Device Object", deviceEntry);
207 SnmpDevice device = null;
208 try {
209 String userInfo = deviceEntry.substring(0, deviceEntry
210 .lastIndexOf('@'));
211 String hostInfo = deviceEntry.substring(deviceEntry
212 .lastIndexOf('@') + 1);
213 String[] infoSplit = userInfo.split(":");
214 String username = infoSplit[0];
215 String password = infoSplit[1];
216 infoSplit = hostInfo.split(":");
217 String hostIp = infoSplit[0];
218 Integer hostPort;
219 try {
220 hostPort = Integer.parseInt(infoSplit[1]);
221 } catch (NumberFormatException nfe) {
222 log.error("Bad Configuration Data: Failed to parse host port number string: "
223 + infoSplit[1]);
224 throw nfe;
225 }
226 String deviceState = infoSplit[2];
227 if (isNullOrEmpty(username) || isNullOrEmpty(password)
228 || isNullOrEmpty(hostIp) || hostPort == 0) {
229 log.warn("Bad Configuration Data: both user and device information parts of Configuration "
230 + deviceEntry + " should be non-nullable");
231 } else {
232 device = new SnmpDevice(hostIp, hostPort, password);
233 if (!isNullOrEmpty(deviceState)) {
234 if (deviceState.toUpperCase().equals(DeviceState.ACTIVE.name())) {
235 device.setDeviceState(DeviceState.ACTIVE);
236 } else if (deviceState.toUpperCase()
237 .equals(DeviceState.INACTIVE.name())) {
238 device.setDeviceState(DeviceState.INACTIVE);
239 } else {
240 log.warn("Device State Information can not be empty, so marking the state as INVALID");
241 device.setDeviceState(DeviceState.INVALID);
242 }
243 } else {
244 log.warn("The device entry do not specify state information, so marking the state as INVALID");
245 device.setDeviceState(DeviceState.INVALID);
246 }
247 }
248 } catch (ArrayIndexOutOfBoundsException aie) {
249 log.error("Error while reading config infromation from the config file: "
250 + "The user, host and device state infomation should be "
251 + "in the order 'userInfo@hostInfo:deviceState'"
252 + deviceEntry, aie);
253 } catch (Exception e) {
254 log.error("Error while parsing config information for the device entry: "
255 + deviceEntry, e);
256 }
257 return device;
258 }
259
260 @Override
261 public void triggerProbe(DeviceId deviceId) {
262 // TODO SNMP devices should be polled at scheduled intervals to retrieve their
263 // reachability status and other details e.g.swVersion, serialNumber,chassis,
264 }
265
266 @Override
267 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
268
269 }
270
271 @Override
272 public boolean isReachable(DeviceId deviceId) {
273 SnmpDevice snmpDevice = snmpDeviceMap.get(deviceId);
274 if (snmpDevice == null) {
275 log.warn("BAD REQUEST: the requested device id: "
276 + deviceId.toString()
277 + " is not associated to any SNMP Device");
278 return false;
279 }
280 return snmpDevice.isReachable();
281 }
282
283 /**
284 * This class is intended to add or remove Configured SNMP Devices. Functionality relies on 'createFlag' and
285 * 'SnmpDevice' content. The functionality runs as a thread and depending on the 'createFlag' value it will create
286 * or remove Device entry from the core.
287 */
288 private class DeviceCreator implements Runnable {
289
290 private SnmpDevice device;
291 private boolean createFlag;
292
293 public DeviceCreator(SnmpDevice device, boolean createFlag) {
294 this.device = device;
295 this.createFlag = createFlag;
296 }
297
298 @Override
299 public void run() {
300 if (createFlag) {
301 log.info("Trying to create Device Info on ONOS core");
302 advertiseDevices();
303 } else {
304 log.info("Trying to remove Device Info on ONOS core");
305 removeDevices();
306 }
307 }
308
309 /**
310 * For each SNMP Device, remove the entry from the device store.
311 */
312 private void removeDevices() {
313 if (device == null) {
314 log.warn("The Request SNMP Device is null, cannot proceed further");
315 return;
316 }
317 try {
318 DeviceId did = getDeviceId();
319 if (!snmpDeviceMap.containsKey(did)) {
320 log.error("BAD Request: 'Currently device is not discovered, "
321 + "so cannot remove/disconnect the device: "
322 + device.deviceInfo() + "'");
323 return;
324 }
325 providerService.deviceDisconnected(did);
326 device.disconnect();
327 snmpDeviceMap.remove(did);
328 delay(EVENTINTERVAL);
329 } catch (URISyntaxException uriSyntaxExcpetion) {
330 log.error("Syntax Error while creating URI for the device: "
331 + device.deviceInfo()
332 + " couldn't remove the device from the store",
333 uriSyntaxExcpetion);
334 }
335 }
336
337 /**
338 * Initialize SNMP Device object, and notify core saying device connected.
339 */
340 private void advertiseDevices() {
341 try {
342 if (device == null) {
343 log.warn("The Request SNMP Device is null, cannot proceed further");
344 return;
345 }
346 device.init();
347 DeviceId did = getDeviceId();
348 ChassisId cid = new ChassisId();
349
Marc De Leenheerc662d322016-02-18 16:05:10 -0800350 SparseAnnotations annotations = DefaultAnnotations.builder()
351 .set(AnnotationKeys.PROTOCOL, SCHEME.toUpperCase())
352 .build();
kmcpeakeb172d5f2015-12-10 11:30:43 +0000353
354 DeviceDescription desc = new DefaultDeviceDescription(
Marc De Leenheerc662d322016-02-18 16:05:10 -0800355 did.uri(), Device.Type.OTHER, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, cid, annotations);
kmcpeakeb172d5f2015-12-10 11:30:43 +0000356
357 desc = populateDescriptionFromDevice(did, desc);
358
359 log.info("Persisting Device " + did.uri().toString());
360
361 snmpDeviceMap.put(did, device);
362 providerService.deviceConnected(did, desc);
363 log.info("Done with Device Info Creation on ONOS core. Device Info: "
364 + device.deviceInfo() + " " + did.uri().toString());
Marc De Leenheerc662d322016-02-18 16:05:10 -0800365
366 // Do port discovery if driver supports it
367 Device d = deviceService.getDevice(did);
368 if (d.is(PortDiscovery.class)) {
369 PortDiscovery portConfig = d.as(PortDiscovery.class);
370 if (portConfig != null) {
371 providerService.updatePorts(did, portConfig.getPorts());
372 }
373 } else {
374 log.warn("No port discovery behaviour for device {}", did);
375 }
376
kmcpeakeb172d5f2015-12-10 11:30:43 +0000377 delay(EVENTINTERVAL);
378 } catch (URISyntaxException e) {
379 log.error("Syntax Error while creating URI for the device: "
380 + device.deviceInfo()
381 + " couldn't persist the device onto the store", e);
382 } catch (Exception e) {
383 log.error("Error while initializing session for the device: "
384 + (device != null ? device.deviceInfo() : null), e);
385 }
386 }
Andrea Campanella3eca4a82016-02-10 17:35:14 -0800387 /**
388 * @deprecated 1.5.0 Falcon, not compliant with ONOS SB and driver architecture.
389 */
390 @Deprecated
kmcpeakeb172d5f2015-12-10 11:30:43 +0000391 private DeviceDescription populateDescriptionFromDevice(DeviceId did, DeviceDescription desc) {
392 String[] deviceComponents = did.toString().split(":");
393 if (deviceComponents.length > 1) {
394 String ipAddress = deviceComponents[1];
395 String port = deviceComponents[2];
396
Marc De Leenheerc662d322016-02-18 16:05:10 -0800397 ISnmpConfiguration config = new V2cSnmpConfiguration();
398 config.setPort(Integer.parseInt(port));
kmcpeakeb172d5f2015-12-10 11:30:43 +0000399
400 try (ISnmpSession session = sessionFactory.createSession(config, ipAddress)) {
401 // Each session will be auto-closed.
Jonathan Hart51539b82015-10-29 09:53:04 -0700402 String deviceOid = session.identifyDevice();
kmcpeakeb172d5f2015-12-10 11:30:43 +0000403
Jonathan Hart51539b82015-10-29 09:53:04 -0700404 if (providers.containsKey(deviceOid)) {
405 desc = providers.get(deviceOid).populateDescription(session, desc);
kmcpeakeb172d5f2015-12-10 11:30:43 +0000406 }
407
408 } catch (IOException | RuntimeException ex) {
409 log.error("Failed to walk device.", ex.getMessage());
410 log.debug("Detailed problem was ", ex);
411 }
412 }
413 return desc;
414 }
415
416 /**
417 * This will build a device id for the device.
418 */
419 private DeviceId getDeviceId() throws URISyntaxException {
Jonathan Hart51539b82015-10-29 09:53:04 -0700420 String additionalSsp = new StringBuilder(
kmcpeakeb172d5f2015-12-10 11:30:43 +0000421 device.getSnmpHost()).append(":")
422 .append(device.getSnmpPort()).toString();
Jonathan Hart51539b82015-10-29 09:53:04 -0700423 return DeviceId.deviceId(new URI(SCHEME, additionalSsp,
kmcpeakeb172d5f2015-12-10 11:30:43 +0000424 null));
425 }
426 }
427
428 protected ISnmpSessionFactory getSessionFactory(ISnmpConfigurationFactory configurationFactory) {
429 return new SnmpSessionFactory(configurationFactory);
430 }
Saurav Dasa2d37502016-03-25 17:50:40 -0700431
432 @Override
433 public void changePortState(DeviceId deviceId, PortNumber portNumber,
434 boolean enable) {
435 // TODO if required
436 }
kmcpeakeb172d5f2015-12-10 11:30:43 +0000437}