blob: fb77c708b3fad276724ca5a046c10dcb5f7bc871 [file] [log] [blame]
kmcpeakeb172d5f2015-12-10 11:30:43 +00001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
kmcpeakeb172d5f2015-12-10 11:30:43 +00003 *
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
Andrea Campanella59b549d2017-04-14 21:58:16 +020018import com.google.common.collect.ImmutableList;
kmcpeakeb172d5f2015-12-10 11:30:43 +000019import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Modified;
kmcpeakeb172d5f2015-12-10 11:30:43 +000023import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
25import org.onlab.packet.ChassisId;
Andrea Campanellac2d754b2016-03-29 17:51:07 -070026import org.onosproject.core.ApplicationId;
27import org.onosproject.core.CoreService;
28import org.onosproject.incubator.net.config.basics.ConfigException;
Marc De Leenheerc662d322016-02-18 16:05:10 -080029import org.onosproject.net.AnnotationKeys;
30import org.onosproject.net.DefaultAnnotations;
kmcpeakeb172d5f2015-12-10 11:30:43 +000031import org.onosproject.net.Device;
32import org.onosproject.net.DeviceId;
33import org.onosproject.net.MastershipRole;
Saurav Dasa2d37502016-03-25 17:50:40 -070034import org.onosproject.net.PortNumber;
Marc De Leenheerc662d322016-02-18 16:05:10 -080035import org.onosproject.net.SparseAnnotations;
Andrea Campanellac2d754b2016-03-29 17:51:07 -070036import org.onosproject.net.config.ConfigFactory;
37import org.onosproject.net.config.NetworkConfigEvent;
38import org.onosproject.net.config.NetworkConfigListener;
39import org.onosproject.net.config.NetworkConfigRegistry;
Andrea Campanella59b549d2017-04-14 21:58:16 +020040import org.onosproject.net.config.basics.SubjectFactories;
kmcpeakeb172d5f2015-12-10 11:30:43 +000041import org.onosproject.net.device.DefaultDeviceDescription;
42import org.onosproject.net.device.DeviceDescription;
Andrea Campanellac2d754b2016-03-29 17:51:07 -070043import org.onosproject.net.device.DeviceDescriptionDiscovery;
kmcpeakeb172d5f2015-12-10 11:30:43 +000044import org.onosproject.net.device.DeviceProvider;
45import org.onosproject.net.device.DeviceProviderRegistry;
46import org.onosproject.net.device.DeviceProviderService;
47import org.onosproject.net.device.DeviceService;
Andrea Campanellac2d754b2016-03-29 17:51:07 -070048import org.onosproject.net.device.DeviceStore;
kmcpeakeb172d5f2015-12-10 11:30:43 +000049import org.onosproject.net.provider.AbstractProvider;
50import org.onosproject.net.provider.ProviderId;
Andrea Campanellac2d754b2016-03-29 17:51:07 -070051import org.onosproject.snmp.SnmpController;
52import org.onosproject.snmp.SnmpDevice;
53import org.onosproject.snmp.ctl.DefaultSnmpDevice;
kmcpeakeb172d5f2015-12-10 11:30:43 +000054import org.osgi.service.component.ComponentContext;
55import org.slf4j.Logger;
56
Andrea Campanella59b549d2017-04-14 21:58:16 +020057import java.util.List;
58import java.util.Set;
Jonathan Hart51539b82015-10-29 09:53:04 -070059import java.util.concurrent.ExecutorService;
60import java.util.concurrent.Executors;
61import java.util.concurrent.TimeUnit;
62
Jonathan Hart51539b82015-10-29 09:53:04 -070063import static org.onlab.util.Tools.groupedThreads;
Andrea Campanellac2d754b2016-03-29 17:51:07 -070064import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
Jonathan Hart51539b82015-10-29 09:53:04 -070065import static org.slf4j.LoggerFactory.getLogger;
66
kmcpeakeb172d5f2015-12-10 11:30:43 +000067/**
68 * Provider which will try to fetch the details of SNMP devices from the core and run a capability discovery on each of
69 * the device.
70 */
71@Component(immediate = true)
72public class SnmpDeviceProvider extends AbstractProvider
73 implements DeviceProvider {
74
75 private final Logger log = getLogger(SnmpDeviceProvider.class);
76
77 private static final String UNKNOWN = "unknown";
Andrea Campanellac2d754b2016-03-29 17:51:07 -070078 private static final String APP_NAME = "org.onosproject.snmp";
Andrea Campanella59b549d2017-04-14 21:58:16 +020079 protected static final String SCHEME = "snmp";
kmcpeakeb172d5f2015-12-10 11:30:43 +000080
kmcpeakeb172d5f2015-12-10 11:30:43 +000081 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanellac2d754b2016-03-29 17:51:07 -070082 protected SnmpController controller;
83
84 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
kmcpeakeb172d5f2015-12-10 11:30:43 +000085 protected DeviceProviderRegistry providerRegistry;
86
87 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
88 protected DeviceService deviceService;
89
90 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanellac2d754b2016-03-29 17:51:07 -070091 protected DeviceStore deviceStore;
kmcpeakeb172d5f2015-12-10 11:30:43 +000092
93 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Andrea Campanellac2d754b2016-03-29 17:51:07 -070094 protected CoreService coreService;
kmcpeakeb172d5f2015-12-10 11:30:43 +000095
Andrea Campanellac2d754b2016-03-29 17:51:07 -070096 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
97 protected NetworkConfigRegistry netCfgService;
kmcpeakeb172d5f2015-12-10 11:30:43 +000098
Andrea Campanella58454b92016-04-01 15:19:00 -070099 protected DeviceProviderService providerService;
100
101 protected ApplicationId appId;
102
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700103 private final ExecutorService deviceBuilderExecutor = Executors
104 .newFixedThreadPool(5, groupedThreads("onos/snmp", "device-creator", log));
kmcpeakeb172d5f2015-12-10 11:30:43 +0000105
Andrea Campanella58454b92016-04-01 15:19:00 -0700106 protected final NetworkConfigListener cfgLister = new InternalNetworkConfigListener();
kmcpeakeb172d5f2015-12-10 11:30:43 +0000107
kmcpeakeb172d5f2015-12-10 11:30:43 +0000108
Andrea Campanella59b549d2017-04-14 21:58:16 +0200109 protected final List<ConfigFactory> factories = ImmutableList.of(
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700110 new ConfigFactory<ApplicationId, SnmpProviderConfig>(APP_SUBJECT_FACTORY,
111 SnmpProviderConfig.class,
112 "devices",
113 true) {
114 @Override
115 public SnmpProviderConfig createConfig() {
116 return new SnmpProviderConfig();
117 }
Andrea Campanella59b549d2017-04-14 21:58:16 +0200118 },
119 new ConfigFactory<DeviceId, SnmpDeviceConfig>(SubjectFactories.DEVICE_SUBJECT_FACTORY,
120 SnmpDeviceConfig.class,
121 SCHEME) {
122 @Override
123 public SnmpDeviceConfig createConfig() {
124 return new SnmpDeviceConfig();
125 }
126 });
kmcpeakeb172d5f2015-12-10 11:30:43 +0000127
kmcpeakeb172d5f2015-12-10 11:30:43 +0000128
129 /**
130 * Creates a provider with the supplier identifier.
131 */
132 public SnmpDeviceProvider() {
133 super(new ProviderId("snmp", "org.onosproject.provider.device"));
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700134 //FIXME multiple type of SNMP sessions
kmcpeakeb172d5f2015-12-10 11:30:43 +0000135 }
136
137 @Activate
138 public void activate(ComponentContext context) {
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700139
kmcpeakeb172d5f2015-12-10 11:30:43 +0000140 providerService = providerRegistry.register(this);
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700141 appId = coreService.registerApplication(APP_NAME);
Andrea Campanella59b549d2017-04-14 21:58:16 +0200142 factories.forEach(netCfgService::registerConfigFactory);
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700143 netCfgService.addListener(cfgLister);
Andrea Campanella59b549d2017-04-14 21:58:16 +0200144 connectDevices();
145 addOrRemoveDevicesConfig();
kmcpeakeb172d5f2015-12-10 11:30:43 +0000146 modified(context);
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700147 log.info("Started");
kmcpeakeb172d5f2015-12-10 11:30:43 +0000148 }
149
150 @Deactivate
151 public void deactivate(ComponentContext context) {
152
kmcpeakeb172d5f2015-12-10 11:30:43 +0000153 try {
Sho SHIMIZUa09e1bb2016-08-01 14:25:25 -0700154 controller.getDevices().forEach(device -> {
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700155 deviceBuilderExecutor.execute(new DeviceFactory(device, false));
156 });
157 deviceBuilderExecutor.awaitTermination(1000, TimeUnit.MILLISECONDS);
kmcpeakeb172d5f2015-12-10 11:30:43 +0000158 } catch (InterruptedException e) {
159 log.error("Device builder did not terminate");
160 }
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700161 deviceBuilderExecutor.shutdownNow();
Andrea Campanella59b549d2017-04-14 21:58:16 +0200162 factories.forEach(netCfgService::unregisterConfigFactory);
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700163 netCfgService.removeListener(cfgLister);
kmcpeakeb172d5f2015-12-10 11:30:43 +0000164 providerRegistry.unregister(this);
165 providerService = null;
166 log.info("Stopped");
167 }
168
169 @Modified
170 public void modified(ComponentContext context) {
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700171 log.info("Modified");
kmcpeakeb172d5f2015-12-10 11:30:43 +0000172 }
173
Andrea Campanella59b549d2017-04-14 21:58:16 +0200174 //Old method to register devices provided via net-cfg under apps/snmp/ tree
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700175 private void addOrRemoveDevicesConfig() {
176 SnmpProviderConfig cfg = netCfgService.getConfig(appId, SnmpProviderConfig.class);
177 if (cfg != null) {
kmcpeakeb172d5f2015-12-10 11:30:43 +0000178 try {
Sho SHIMIZUa09e1bb2016-08-01 14:25:25 -0700179 cfg.getDevicesInfo().forEach(info -> {
Andrea Campanella59b549d2017-04-14 21:58:16 +0200180 buildDevice(new DefaultSnmpDevice(info.ip().toString(),
181 info.port(), info.username(),
182 info.password()));
183
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700184 });
185 } catch (ConfigException e) {
186 log.error("Cannot read config error " + e);
kmcpeakeb172d5f2015-12-10 11:30:43 +0000187 }
kmcpeakeb172d5f2015-12-10 11:30:43 +0000188 }
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700189 }
190
Andrea Campanella59b549d2017-04-14 21:58:16 +0200191 //Method to register devices provided via net-cfg under devices/ tree
192 private void connectDevices() {
193 Set<DeviceId> deviceSubjects =
194 netCfgService.getSubjects(DeviceId.class, SnmpDeviceConfig.class);
195 deviceSubjects.forEach(deviceId -> {
196 SnmpDeviceConfig config =
197 netCfgService.getConfig(deviceId, SnmpDeviceConfig.class);
198 buildDevice(new DefaultSnmpDevice(config.ip().toString(),
199 config.port(), config.username(), config.password()));
200 });
201 }
202
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700203 private void buildDevice(SnmpDevice device) {
204 if (device != null) {
205 log.debug("Device Detail:host={}, port={}, state={}",
206 device.getSnmpHost(),
207 device.getSnmpPort(),
208 device.isReachable());
209 if (device.isReachable()) {
210 deviceBuilderExecutor.execute(new DeviceFactory(device, true));
211 } else {
212 deviceBuilderExecutor.execute(new DeviceFactory(device, false));
213 }
214 }
kmcpeakeb172d5f2015-12-10 11:30:43 +0000215 }
216
217 @Override
218 public void triggerProbe(DeviceId deviceId) {
219 // TODO SNMP devices should be polled at scheduled intervals to retrieve their
220 // reachability status and other details e.g.swVersion, serialNumber,chassis,
221 }
222
223 @Override
224 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700225 // TODO Implement Masterhsip Service
kmcpeakeb172d5f2015-12-10 11:30:43 +0000226 }
227
228 @Override
229 public boolean isReachable(DeviceId deviceId) {
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700230 SnmpDevice snmpDevice = controller.getDevice(deviceId);
kmcpeakeb172d5f2015-12-10 11:30:43 +0000231 if (snmpDevice == null) {
232 log.warn("BAD REQUEST: the requested device id: "
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700233 + deviceId.toString()
234 + " is not associated to any SNMP Device");
kmcpeakeb172d5f2015-12-10 11:30:43 +0000235 return false;
236 }
237 return snmpDevice.isReachable();
238 }
239
240 /**
241 * This class is intended to add or remove Configured SNMP Devices. Functionality relies on 'createFlag' and
242 * 'SnmpDevice' content. The functionality runs as a thread and depending on the 'createFlag' value it will create
243 * or remove Device entry from the core.
244 */
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700245 //FIXME consider rework.
246 private class DeviceFactory implements Runnable {
kmcpeakeb172d5f2015-12-10 11:30:43 +0000247
248 private SnmpDevice device;
249 private boolean createFlag;
250
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700251 public DeviceFactory(SnmpDevice device, boolean createFlag) {
kmcpeakeb172d5f2015-12-10 11:30:43 +0000252 this.device = device;
253 this.createFlag = createFlag;
254 }
255
256 @Override
257 public void run() {
258 if (createFlag) {
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700259 log.debug("Trying to create Device Info on ONOS core");
kmcpeakeb172d5f2015-12-10 11:30:43 +0000260 advertiseDevices();
261 } else {
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700262 log.debug("Trying to remove Device Info on ONOS core");
kmcpeakeb172d5f2015-12-10 11:30:43 +0000263 removeDevices();
264 }
265 }
266
267 /**
268 * For each SNMP Device, remove the entry from the device store.
269 */
270 private void removeDevices() {
271 if (device == null) {
272 log.warn("The Request SNMP Device is null, cannot proceed further");
273 return;
274 }
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700275 DeviceId did = device.deviceId();
276 if (controller.getDevice(did) == null) {
277 log.error("BAD Request: 'Currently device is not discovered, "
278 + "so cannot remove/disconnect the device: "
279 + device.deviceInfo() + "'");
280 return;
kmcpeakeb172d5f2015-12-10 11:30:43 +0000281 }
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700282 providerService.deviceDisconnected(did);
283 device.disconnect();
284 controller.removeDevice(did);
kmcpeakeb172d5f2015-12-10 11:30:43 +0000285 }
286
287 /**
288 * Initialize SNMP Device object, and notify core saying device connected.
289 */
290 private void advertiseDevices() {
291 try {
292 if (device == null) {
293 log.warn("The Request SNMP Device is null, cannot proceed further");
294 return;
295 }
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700296 DeviceId did = device.deviceId();
kmcpeakeb172d5f2015-12-10 11:30:43 +0000297 ChassisId cid = new ChassisId();
298
Marc De Leenheerc662d322016-02-18 16:05:10 -0800299 SparseAnnotations annotations = DefaultAnnotations.builder()
300 .set(AnnotationKeys.PROTOCOL, SCHEME.toUpperCase())
301 .build();
kmcpeakeb172d5f2015-12-10 11:30:43 +0000302
303 DeviceDescription desc = new DefaultDeviceDescription(
Marc De Leenheerc662d322016-02-18 16:05:10 -0800304 did.uri(), Device.Type.OTHER, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, cid, annotations);
kmcpeakeb172d5f2015-12-10 11:30:43 +0000305
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700306 log.debug("Persisting Device " + did.uri().toString());
kmcpeakeb172d5f2015-12-10 11:30:43 +0000307
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700308 controller.addDevice(did, device);
kmcpeakeb172d5f2015-12-10 11:30:43 +0000309 providerService.deviceConnected(did, desc);
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700310 log.info("Added device to ONOS core. Device Info: "
311 + device.deviceInfo() + " " + did.uri().toString());
312 //FIXME this description will be populated only if driver is pushed from outside
313 // becuase otherwise default driver is used
Marc De Leenheerc662d322016-02-18 16:05:10 -0800314 Device d = deviceService.getDevice(did);
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700315 if (d.is(DeviceDescriptionDiscovery.class)) {
316 DeviceDescriptionDiscovery descriptionDiscovery = d.as(DeviceDescriptionDiscovery.class);
317 DeviceDescription description = descriptionDiscovery.discoverDeviceDetails();
Andrea Campanella6c71a052016-04-22 11:56:31 -0700318 if (description != null) {
319 deviceStore.createOrUpdateDevice(
320 new ProviderId("snmp", "org.onosproject.provider.device"),
321 did, description);
322 } else {
323 log.info("No other description given for device {}", d.id());
324 }
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700325 providerService.updatePorts(did, descriptionDiscovery.discoverPortDetails());
Marc De Leenheerc662d322016-02-18 16:05:10 -0800326 } else {
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700327 log.warn("No populate description and ports behaviour for device {}", did);
Marc De Leenheerc662d322016-02-18 16:05:10 -0800328 }
kmcpeakeb172d5f2015-12-10 11:30:43 +0000329 } catch (Exception e) {
330 log.error("Error while initializing session for the device: "
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700331 + (device != null ? device.deviceInfo() : null), e);
kmcpeakeb172d5f2015-12-10 11:30:43 +0000332 }
333 }
kmcpeakeb172d5f2015-12-10 11:30:43 +0000334 }
335
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700336 private class InternalNetworkConfigListener implements NetworkConfigListener {
337
338
339 @Override
340 public void event(NetworkConfigEvent event) {
Andrea Campanella59b549d2017-04-14 21:58:16 +0200341 if (event.configClass().equals(SnmpDeviceConfig.class)) {
342 connectDevices();
343 } else {
344 log.warn("Injecting device via this Json is deprecated, " +
345 "please put configuration under devices/");
346 addOrRemoveDevicesConfig();
347 }
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700348 }
349
350 @Override
351 public boolean isRelevant(NetworkConfigEvent event) {
Andrea Campanella59b549d2017-04-14 21:58:16 +0200352 return (event.configClass().equals(SnmpDeviceConfig.class) ||
353 event.configClass().equals(SnmpProviderConfig.class)) &&
Andrea Campanellac2d754b2016-03-29 17:51:07 -0700354 (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
355 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
356 }
kmcpeakeb172d5f2015-12-10 11:30:43 +0000357 }
Saurav Dasa2d37502016-03-25 17:50:40 -0700358
359 @Override
360 public void changePortState(DeviceId deviceId, PortNumber portNumber,
361 boolean enable) {
362 // TODO if required
363 }
kmcpeakeb172d5f2015-12-10 11:30:43 +0000364}