blob: 1b29e001e8345b44226dd9647820d16a12e10e71 [file] [log] [blame]
/*
* Copyright 2017-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.provider.snmp.alarm.impl;
import com.google.common.collect.ImmutableList;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Modified;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.onlab.packet.IpAddress;
import org.onosproject.cluster.ClusterService;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.incubator.net.faultmanagement.alarm.Alarm;
import org.onosproject.incubator.net.faultmanagement.alarm.AlarmProvider;
import org.onosproject.incubator.net.faultmanagement.alarm.AlarmProviderRegistry;
import org.onosproject.incubator.net.faultmanagement.alarm.AlarmProviderService;
import org.onosproject.incubator.net.faultmanagement.alarm.DeviceAlarmConfig;
import org.onosproject.mastership.MastershipEvent;
import org.onosproject.mastership.MastershipListener;
import org.onosproject.mastership.MastershipService;
import org.onosproject.net.DeviceId;
import org.onosproject.net.device.DeviceEvent;
import org.onosproject.net.device.DeviceListener;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.driver.DefaultDriverData;
import org.onosproject.net.driver.DefaultDriverHandler;
import org.onosproject.net.driver.Driver;
import org.onosproject.net.driver.DriverData;
import org.onosproject.net.driver.DriverService;
import org.onosproject.net.provider.AbstractProvider;
import org.onosproject.net.provider.ProviderId;
import org.onosproject.snmp.SnmpController;
import org.onosproject.snmp.SnmpDevice;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.snmp4j.CommandResponder;
import org.snmp4j.CommandResponderEvent;
import org.snmp4j.Snmp;
import org.snmp4j.TransportMapping;
import org.snmp4j.smi.Address;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.TcpAddress;
import org.snmp4j.smi.UdpAddress;
import org.snmp4j.transport.DefaultTcpTransportMapping;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import java.io.IOException;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Provider which will listen for traps generated SNMP devices and configure
* the devices with the Ip and Protocol fo the end point to send traps to.
*/
@Component(immediate = true)
public class SnmpAlarmProvider extends AbstractProvider
implements AlarmProvider {
public static final String COLON = ":";
private final Logger log = getLogger(SnmpAlarmProvider.class);
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected SnmpController controller;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected AlarmProviderRegistry providerRegistry;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected DriverService driverService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected MastershipService mastershipService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected ClusterService clusterService;
private static final String APP_NAME = "org.onosproject.snmp";
private static final String SCHEME = "snmp";
protected AlarmProviderService providerService;
protected ApplicationId appId;
protected final DeviceListener deviceListener = new InternalDeviceListener();
protected final MastershipListener mastershipListener = new InternalMastershipListener();
protected final CommandResponder internalCommandResponder = new InternalCommandResponder();
private IpAddress localIp;
private final Map<Integer, Snmp> portSessionMap = new HashMap<>();
private Snmp snmp;
/**
* Creates a provider with the supplier identifier.
*/
public SnmpAlarmProvider() {
super(new ProviderId("snmp", "org.onosproject.provider.alarm"));
}
@Activate
public void activate(ComponentContext context) {
providerService = providerRegistry.register(this);
appId = coreService.registerApplication(APP_NAME);
deviceService.addListener(deviceListener);
mastershipService.addListener(mastershipListener);
controller.getDevices().stream().forEach(d -> {
triggerProbe(d.deviceId());
});
localIp = clusterService.getLocalNode().ip();
log.info("Started");
}
@Deactivate
public void deactivate(ComponentContext context) {
for (Map.Entry<Integer, Snmp> entry : portSessionMap.entrySet()) {
try {
entry.getValue().close();
} catch (IOException e) {
log.warn("Can't close SNMP notification session on Ip {} and Port {}",
localIp, entry.getKey(), e);
}
}
mastershipService.addListener(mastershipListener);
deviceService.removeListener(deviceListener);
providerRegistry.unregister(this);
providerService = null;
log.info("Stopped");
}
@Modified
public void modified() {
log.info("Modified");
}
@Override
public void triggerProbe(DeviceId deviceId) {
requestTraps(deviceId);
configureListeningConnection(controller.getDevice(deviceId));
}
private void configureListeningConnection(SnmpDevice device) {
int notificationPort = device.getNotificationPort();
if (notificationPort == 0 || portSessionMap.containsKey(notificationPort)) {
return;
}
String protocol = device.getProtocol();
try {
Address listenAddress = GenericAddress.parse(protocol +
":" + localIp.toString() +
"/" + notificationPort);
TransportMapping tm;
if (protocol == GenericAddress.TYPE_TCP) {
tm = new DefaultTcpTransportMapping((TcpAddress) listenAddress);
} else {
tm = new DefaultUdpTransportMapping((UdpAddress) listenAddress);
}
snmp = new Snmp(tm);
snmp.addCommandResponder(internalCommandResponder);
snmp.listen();
log.info("Initiated SNMP notification connection on Ip {} and Port {}",
localIp.toString(), notificationPort);
} catch (IOException e) {
log.error("Can't initiate SNMP transport on Protocol {}, Ip {} and Port {}",
protocol, localIp, notificationPort, e);
return;
}
portSessionMap.put(notificationPort, snmp);
}
private boolean requestTraps(DeviceId deviceId) {
SnmpDevice device = controller.getDevice(deviceId);
DeviceAlarmConfig alarmTranslator = getAlarmTranslator(device);
if (alarmTranslator != null) {
return alarmTranslator.configureDevice(localIp, device.getNotificationPort(),
device.getNotificationProtocol());
} else {
log.warn("Device {} does not support alarms.", deviceId);
return false;
}
}
private DeviceAlarmConfig getAlarmTranslator(SnmpDevice device) {
Driver deviceDriver = driverService.getDriver(device.deviceId());
if (deviceDriver != null && deviceDriver.hasBehaviour(DeviceAlarmConfig.class)) {
DriverData driverData = new DefaultDriverData(deviceDriver, device.deviceId());
DeviceAlarmConfig alarmTranslator = deviceDriver
.createBehaviour(driverData, DeviceAlarmConfig.class);
alarmTranslator.setHandler(new DefaultDriverHandler(driverData));
return alarmTranslator;
} else {
log.warn("Device does not support alarm {}", device.deviceId());
}
return null;
}
private class InternalCommandResponder implements CommandResponder {
@Override
public void processPdu(CommandResponderEvent event) {
try {
log.debug("received trap {}", event);
String[] deviceInfo = event.getPeerAddress().toString().split("/");
//TODO This should be done via device service.
//searching only for Ip since the port from which the trap is sent
// could be different from the one used for SNMP
SnmpDevice device = controller.getDevice(new URI(deviceInfo[0]));
if (device != null) {
DeviceId deviceId = DeviceId.deviceId(SCHEME + COLON + deviceInfo[0]
+ COLON + device.getSnmpPort());
DeviceAlarmConfig alarmTranslator = getAlarmTranslator(controller.getDevice(deviceId));
if (alarmTranslator != null) {
Set<Alarm> alarms = alarmTranslator.translateAlarms(ImmutableList.of(event));
providerService.updateAlarmList(deviceId, alarms);
} else {
log.warn("Device {} does not support alarm", device.deviceId());
}
} else {
log.error("Device {} does not exist in ONOS SNMP subsystem", deviceInfo[0]);
}
//Catching generic exception due to otherwise hidden URISyntax
} catch (Exception e) {
log.error("Exception while processing PDU {}", e);
}
}
}
private class InternalDeviceListener implements DeviceListener {
@Override
public void event(DeviceEvent event) {
DeviceId deviceId = event.subject().id();
switch (event.type()) {
case DEVICE_ADDED:
triggerProbe(deviceId);
break;
case DEVICE_UPDATED:
triggerProbe(deviceId);
break;
default:
break;
}
}
@Override
public boolean isRelevant(DeviceEvent event) {
return event.subject().id().toString().startsWith(SCHEME)
&& mastershipService.isLocalMaster(event.subject().id());
}
}
private class InternalMastershipListener implements MastershipListener {
@Override
public void event(MastershipEvent event) {
triggerProbe(event.subject());
}
@Override
public boolean isRelevant(MastershipEvent event) {
return event.subject().toString().startsWith(SCHEME)
&& mastershipService.isLocalMaster(event.subject());
}
}
}