/*
* Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
* Copyright (c) 2015 Tata Consultancy Services, Inc. and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.tsdr.sdc;
import com.google.common.base.Optional;
import com.google.common.util.concurrent.CheckedFuture;
import java.io.PrintStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Future;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
import org.opendaylight.snmp.plugin.internal.SNMPImpl;
import org.opendaylight.yang.gen.v1.opendaylight.tsdr.rev150219.DataCategory;
import org.opendaylight.yang.gen.v1.opendaylight.tsdr.rev150219.tsdrrecord.RecordKeys;
import org.opendaylight.yang.gen.v1.opendaylight.tsdr.rev150219.tsdrrecord.RecordKeysBuilder;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.smiv2._if.mib.rev000614.interfaces.group.IfEntry;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.tsdr.collector.spi.rev150915.InsertTSDRMetricRecordInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.tsdr.collector.spi.rev150915.InsertTSDRMetricRecordInputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.tsdr.collector.spi.rev150915.TsdrCollectorSpiService;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.tsdr.collector.spi.rev150915.inserttsdrmetricrecord.input.TSDRMetricRecord;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.tsdr.collector.spi.rev150915.inserttsdrmetricrecord.input.TSDRMetricRecordBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.tsdr.snmp.data.collector.rev151013.SetPollingIntervalInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.tsdr.snmp.data.collector.rev151013.SnmpMetric;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.tsdr.snmp.data.collector.rev151013.TSDRSnmpDataCollectorConfig;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.tsdr.snmp.data.collector.rev151013.TSDRSnmpDataCollectorConfigBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.tsdr.snmp.data.collector.rev151013.TsdrSnmpDataCollectorService;
import org.opendaylight.yang.gen.v1.urn.opendaylight.snmp.rev140922.GetInterfacesInputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.snmp.rev140922.GetInterfacesOutput;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Sharon Aicler(saichler@gmail.com)
* @author Trapti Khandelwal(trapti.khandelwal@tcs.com)
* @author Razi Ahmed(ahmed.razi@tcs.com)
**/
public class SNMPDataCollector implements TsdrSnmpDataCollectorService {
private static final Logger logger = LoggerFactory.getLogger(SNMPDataCollector.class);
private boolean running = true;
// The reference to the the RPC registry to store the data
private final DataBroker dataBroker;
private final RpcProviderRegistry rpcRegistry;
private TSDRSnmpDataCollectorConfig config = null;
protected Object pollerSyncObject = new Object();
private TsdrCollectorSpiService collectorSPIService = null;
private static final String COLLECTOR_CODE_NAME = SNMPDataCollector.class.getSimpleName();
private static final long pollingInterval=300000l;
private List<TSDRMetricRecord> tsdrMetricRecordList = new LinkedList<>();
public SNMPDataCollector(DataBroker dataBroker,RpcProviderRegistry rpcRegistry) {
log("TSDR SNMP Collector Started", INFO);
this.dataBroker = dataBroker;
this.rpcRegistry = rpcRegistry;
TSDRSnmpDataCollectorConfigBuilder b = new TSDRSnmpDataCollectorConfigBuilder();
b.setPollingInterval(pollingInterval);
this.config = b.build();
saveConfigData();
new TSDRSNMPInterfacePoller(this);
new StoringThread();
}
public void loadConfigData() {
// try to load the configuration data from the configuration data store
ReadOnlyTransaction rot = null;
try {
InstanceIdentifier<TSDRSnmpDataCollectorConfig> cid = InstanceIdentifier
.create(TSDRSnmpDataCollectorConfig.class);
rot = this.dataBroker.newReadOnlyTransaction();
CheckedFuture<Optional<TSDRSnmpDataCollectorConfig>, ReadFailedException> read = rot
.read(LogicalDatastoreType.CONFIGURATION, cid);
if (read != null && read.get() != null) {
if (read.get().isPresent()) {
this.config = read.get().get();
}
}
} catch (Exception err) {
log("Failed to read TSDR Data Collection configuration from data store, using defaults.",
WARNING);
} finally {
if (rot != null) {
rot.close();
}
}
}
public RpcResult<GetInterfacesOutput> loadGetInterfacesData(Ipv4Address ip, String community) {
// fetch data from getInterfaces
RpcResult<GetInterfacesOutput> result = null;
SNMPImpl snmpImpl = new SNMPImpl(rpcRegistry);
try
{
GetInterfacesInputBuilder input = new GetInterfacesInputBuilder();
input.setCommunity(community);
input.setIpAddress(ip);
Future<RpcResult<GetInterfacesOutput>> resultFuture = snmpImpl.getInterfaces(input.build());
result = resultFuture.get();
result.isSuccessful();
return result;
}
catch (Exception err) {
log("Failed to get interfaces data from SNMP"+err.toString(), ERROR);
return null;
}
}
public void insertInterfacesEntries(Ipv4Address ip, RpcResult<GetInterfacesOutput> result){
for(IfEntry entry : result.getResult().getIfEntry())
{
for(SnmpMetric snmpMetric:SnmpMetric.values()){
TSDRMetricRecordBuilder b = new TSDRMetricRecordBuilder();
b.setMetricName(snmpMetric.name());
b.setTSDRDataCategory(DataCategory.SNMPINTERFACES);
b.setNodeID(ip.getValue().toString());
ArrayList<RecordKeys> list =new ArrayList<RecordKeys>(3);
RecordKeysBuilder recordKeyB = new RecordKeysBuilder();
recordKeyB.setKeyName("ifIndex");
recordKeyB.setKeyValue(String.valueOf(entry.getIfIndex().getValue()));
list.add(recordKeyB.build());
recordKeyB.setKeyName("ifName");
recordKeyB.setKeyValue(String.valueOf(entry.getIfType()));
list.add(recordKeyB.build());
recordKeyB.setKeyName("SnmpMetric");
recordKeyB.setKeyValue(snmpMetric.name());
list.add(recordKeyB.build());
b.setRecordKeys(list);
b.setTimeStamp(System.currentTimeMillis());
switch(snmpMetric) {
// do not need to be stored as they do not change often
/* case MTU:
b.setMetricValue(new BigDecimal(entry.getIfMtu()));
break;
case IfSpeed:
b.setMetricValue(new BigDecimal(entry.getIfSpeed().getValue()));
break;*/
case IfInNUcastPkts:
b.setMetricValue(new BigDecimal(entry.getIfInNUcastPkts().getValue()));
break;
case IfInDiscards:
b.setMetricValue(new BigDecimal(entry.getIfInDiscards().getValue()));
break;
case IfInErrors:
b.setMetricValue(new BigDecimal(entry.getIfInErrors().getValue()));
break;
case IfInOctets:
b.setMetricValue(new BigDecimal(entry.getIfInOctets().getValue()));
break;
case IfInUnknownProtos:
b.setMetricValue(new BigDecimal(entry.getIfInUnknownProtos().getValue()));
break;
case IfInUcastPkts:
b.setMetricValue(new BigDecimal(entry.getIfInUcastPkts().getValue()));
break;
case IfOutQLen:
b.setMetricValue(new BigDecimal(entry.getIfOutQLen().getValue()));
break;
case IfOutNUcastPkts:
b.setMetricValue(new BigDecimal(entry.getIfOutNUcastPkts().getValue()));
break;
case IfOutErrors:
b.setMetricValue(new BigDecimal(entry.getIfOutErrors().getValue()));
break;
case IfOutDiscards:
b.setMetricValue(new BigDecimal(entry.getIfOutDiscards().getValue()));
break;
case IfOutUcastPkts:
b.setMetricValue(new BigDecimal(entry.getIfOutUcastPkts().getValue()));
break;
case IfOutOctets:
b.setMetricValue(new BigDecimal(entry.getIfOutOctets().getValue()));
break;
case IfOperStatus:
b.setMetricValue(new BigDecimal(entry.getIfOperStatus().getIntValue()));
break;
case IfAdminStatus:
b.setMetricValue(new BigDecimal(entry.getIfAdminStatus().getIntValue()));
break;
}
synchronized(SNMPDataCollector.class) {
tsdrMetricRecordList.add(b.build());
}
}
}
}
public void saveConfigData() {
try {
InstanceIdentifier<TSDRSnmpDataCollectorConfig> cid = InstanceIdentifier
.create(TSDRSnmpDataCollectorConfig.class);
WriteTransaction wrt = this.dataBroker.newWriteOnlyTransaction();
wrt.put(LogicalDatastoreType.CONFIGURATION, cid, this.config);
wrt.submit();
} catch (Exception err) {
log("Failed to write TSDR Data Collection configuration to data store.",
WARNING);
}
}
public TSDRSnmpDataCollectorConfig getConfigData() {
return this.config;
}
public void shutdown() {
this.running = false;
synchronized(SNMPDataCollector.this.pollerSyncObject){
SNMPDataCollector.this.pollerSyncObject.notifyAll();
}
synchronized(SNMPDataCollector.this){
SNMPDataCollector.this.notifyAll();
}
}
// This class is the storing thread, every 30 seconds it will wake up and
// iterate over the builder container array and create
// metric data list out of the container builders, wrap it up as input for
// the RPC and invoke the storage RPC method.
private class StoringThread extends Thread {
public StoringThread() {
this.setName("TSDR SNMP Storing Thread");
this.setDaemon(true);
this.start();
log("SNMP Storing Thread Started", INFO);
}
public void run() {
while (running) {
synchronized (SNMPDataCollector.this) {
try {
/*
* We wait for 2x the polling interval just for the case
* where the polling thread is dead and there will be no
* thread to wake this thread up if we do "wait()", e.g.
* to avoid "stuck" thread. Disregarding the case where
* storing will take more than the polling interval, we
* have bigger issues in that case...:o)
*/
SNMPDataCollector.this.wait(getConfigData().getPollingInterval() * 2);
} catch (InterruptedException err) {
log("SNMP Storing Thread Interrupted.", ERROR);
}
}
if(!running)
{
break;
}
try {
try {
InsertTSDRMetricRecordInputBuilder input = new InsertTSDRMetricRecordInputBuilder();
synchronized (SNMPDataCollector.class) {
List<TSDRMetricRecord> list = tsdrMetricRecordList;
tsdrMetricRecordList = new LinkedList<>();
input.setTSDRMetricRecord(list);
}
input.setCollectorCodeName(COLLECTOR_CODE_NAME);
store(input.build());
} catch (Exception err) {
log("Fail to store data due to the following exception:", ERROR);
log(err);
}
} catch (Exception err) {
log("Fail to iterate over builder containers due to the following error:", ERROR);
log(err);
}
}
}
}
// Invoke the storage rpc method
private void store(InsertTSDRMetricRecordInput input) {
if(this.collectorSPIService==null){
this.collectorSPIService = this.rpcRegistry
.getRpcService(TsdrCollectorSpiService.class);
}
this.collectorSPIService.insertTSDRMetricRecord(input);
log("Data Storage Called from SNMP Collector", DEBUG);
}
public TsdrCollectorSpiService getTSDRService(){
if(this.collectorSPIService==null){
this.collectorSPIService = this.rpcRegistry
.getRpcService(TsdrCollectorSpiService.class);
}
return this.collectorSPIService;
}
// For debugging, enable the ability to output to a different file to avoid
// looking for TSDR logs in the main log.
public static PrintStream out = null;
public static final int INFO = 1;
public static final int DEBUG = 2;
public static final int ERROR = 3;
public static final int WARNING = 4;
public static synchronized void log(Exception e) {
logger.error(e.getMessage(), e);
}
public static synchronized void log(String str, int type) {
switch (type) {
case INFO:
logger.info(str);
break;
case DEBUG:
logger.debug(str);
break;
case ERROR:
logger.error(str);
break;
case WARNING:
logger.warn(str);
break;
default:
logger.debug(str);
}
}
public boolean isRunning() {
return this.running;
}
@Override
public Future<RpcResult<Void>> setPollingInterval(SetPollingIntervalInput input) {
TSDRSnmpDataCollectorConfigBuilder builder = new TSDRSnmpDataCollectorConfigBuilder();
builder.setPollingInterval(input.getInterval());
this.config = builder.build();
saveConfigData();
RpcResultBuilder<Void> rpc = RpcResultBuilder.success();
return rpc.buildFuture();
}
}