/*
* Copyright (c) 2016 TethrNet Technology Co.Ltd and others. All rights reserved.
* Copyright (c) 2016 Cisco Systems, 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.syslogs.server.datastore;
import com.google.common.base.Optional;
import com.google.common.util.concurrent.CheckedFuture;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.binding.api.ReadTransaction;
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.tsdr.syslogs.server.decoder.Message;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.tsdr.syslog.collector.rev151007.*;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.tsdr.syslog.collector.rev151007.show.register.filter.output.RegisteredSyslogFilter;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.tsdr.syslog.collector.rev151007.show.register.filter.output.RegisteredSyslogFilterBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.tsdr.syslog.collector.rev151007.show.register.filter.output.registered.syslog.filter.RegisteredFilterEntity;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.tsdr.syslog.collector.rev151007.show.register.filter.output.registered.syslog.filter.RegisteredFilterEntityBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.tsdr.syslog.collector.rev151007.syslog.dispatcher.*;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.tsdr.syslog.collector.rev151007.syslog.dispatcher.syslog.filter.*;
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;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* This SyslogDatastoreManager handles the initialization of the data
* structure of Syslog collector. it also implements all its RPC and
* provides multi-threads to filter the incoming Syslog messages.
*
* @author Wei Lai(weilai@tethrnet.com)
* @author Wenbo Hu(wenbhu@tethrnet.com)
*/
public class SyslogDatastoreManager implements TsdrSyslogCollectorService {
private static SyslogDatastoreManager INSTANCE;
private static final Logger LOG = LoggerFactory.getLogger(TsdrSyslogCollectorService.class);
private static AtomicInteger messageID = new AtomicInteger(0);
private final ThreadPoolExecutor threadPool;
private DataBroker db;
private Map<String, String> registerMap = new HashMap<>();
private Map<String, RegisteredListener> listenerMap = new HashMap<>();
private SyslogDatastoreManager(int coreThreadPoolSize, int maxThreadpoolSize, long keepAliveTime, int queueSize) {
this.db = null;
this.threadPool = new ThreadPoolExecutor(coreThreadPoolSize, maxThreadpoolSize, keepAliveTime, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(queueSize));
this.threadPool.prestartAllCoreThreads();
}
public static SyslogDatastoreManager getInstance() {
return INSTANCE;
}
public static SyslogDatastoreManager getInstance(int coreThreadPoolSize, int maxThreadpoolSize, long keepAliveTime, int queueSize) {
INSTANCE = new SyslogDatastoreManager(coreThreadPoolSize, maxThreadpoolSize, keepAliveTime, queueSize);
return INSTANCE;
}
public void setDataBroker(DataBroker db) {
if (this.db == null) {
this.db = db;
this.initializeDataTree();
} else {
LOG.warn("Syslog DataStore Manager has been set! Ignore new databroker");
}
}
public void execute(String ipaddress, Message message) {
int mid = SyslogDatastoreManager.messageID.addAndGet(1);
INSTANCE.threadPool.execute(new WorkerThread(mid, ipaddress, message));
}
private void initializeDataTree() {
LOG.info("Preparing to initialize the greeting registry");
WriteTransaction transaction = db.newWriteOnlyTransaction();
InstanceIdentifier<SyslogDispatcher> iid = InstanceIdentifier.create(SyslogDispatcher.class);
SyslogDispatcher dispatcher = new SyslogDispatcherBuilder().build();
transaction.put(LogicalDatastoreType.CONFIGURATION, iid, dispatcher);
transaction.put(LogicalDatastoreType.OPERATIONAL, iid, dispatcher);
transaction.submit();
}
@Override
public Future<RpcResult<ShowThreadpoolConfigurationOutput>> showThreadpoolConfiguration() {
int currentThreadpoolQueueSize = threadPool.getQueue().size();
int currentThreadpoolQueueRemainingCapacity = threadPool.getQueue().remainingCapacity();
long currentThreadpoolKeepAliveTime = threadPool.getKeepAliveTime(TimeUnit.SECONDS);
LOG.info("currentThreadpoolKeepAliveTime" + currentThreadpoolKeepAliveTime);
LOG.info("currentThreadpoolQueueSize" + currentThreadpoolQueueSize);
LOG.info("currentThreadpoolQueueRemainingCapacity" + currentThreadpoolQueueRemainingCapacity);
ShowThreadpoolConfigurationOutput output = new ShowThreadpoolConfigurationOutputBuilder()
.setCoreThreadNumber(threadPool.getCorePoolSize())
.setMaxThreadNumber(threadPool.getMaximumPoolSize())
.setCurrentAliveThreadNumber(threadPool.getPoolSize())
.setKeepAliveTime((int) (currentThreadpoolKeepAliveTime))
.setQueueRemainingCapacity(currentThreadpoolQueueRemainingCapacity)
.setQueueUsedCapacity(currentThreadpoolQueueSize)
.build();
return RpcResultBuilder.success(output).buildFuture();
}
@Override
public Future<RpcResult<DeleteRegisteredFilterOutput>> deleteRegisteredFilter(DeleteRegisteredFilterInput input) {
String listenerID = registerMap.get(input.getFilterId());
RegisteredListener registeredListener = listenerMap.get(listenerID);
boolean closeResult = registeredListener.close();
if (!closeResult){
LOG.error("listener registration close failed");
DeleteRegisteredFilterOutput output = new DeleteRegisteredFilterOutputBuilder()
.setResult("listener registration close failed")
.build();
return RpcResultBuilder.success(output).buildFuture();
}
InstanceIdentifier<SyslogListener> syslogListenerIID =
InstanceIdentifier.create(SyslogDispatcher.class)
.child(SyslogListener.class, new SyslogListenerKey(listenerID));
WriteTransaction deleteTransaction = db.newWriteOnlyTransaction();
InstanceIdentifier<SyslogFilter> filterIID = InstanceIdentifier.create(SyslogDispatcher.class)
.child(SyslogFilter.class, new SyslogFilterKey(input.getFilterId()));
deleteTransaction.delete(LogicalDatastoreType.CONFIGURATION, filterIID);
deleteTransaction.delete(LogicalDatastoreType.OPERATIONAL, syslogListenerIID);
try {
deleteTransaction.submit().get();
} catch (Exception e) {
LOG.info("filter delete failed");
DeleteRegisteredFilterOutput output = new DeleteRegisteredFilterOutputBuilder()
.setResult("filter delete failed")
.build();
return RpcResultBuilder.success(output).buildFuture();
}
DeleteRegisteredFilterOutput output = new DeleteRegisteredFilterOutputBuilder()
.setResult("filter delete successfully")
.build();
return RpcResultBuilder.success(output).buildFuture();
}
public Map<String, String> getRegisterMap() {
return registerMap;
}
public void setRegisterMap(Map<String, String> registerMap) {
this.registerMap = registerMap;
}
public Map<String, RegisteredListener> getListenerMap() {
return listenerMap;
}
public void setListenerMap(Map<String, RegisteredListener> listenerMap) {
this.listenerMap = listenerMap;
}
@Override
public Future<RpcResult<ShowRegisterFilterOutput>> showRegisterFilter() {
ReadTransaction transaction = db.newReadOnlyTransaction();
InstanceIdentifier<SyslogDispatcher> iid =
InstanceIdentifier.create(SyslogDispatcher.class);
CheckedFuture<Optional<SyslogDispatcher>, ReadFailedException> future =
transaction.read(LogicalDatastoreType.CONFIGURATION, iid);
Optional<SyslogDispatcher> optional = Optional.absent();
try {
optional = future.checkedGet();
} catch (ReadFailedException e) {
LOG.warn("Reading Filter failed");
ShowRegisterFilterOutput output = new ShowRegisterFilterOutputBuilder()
.setResult("Reading Filter failed")
.build();
return RpcResultBuilder.success(output).buildFuture();
}
if (optional.isPresent() && !(optional.get().getSyslogFilter().isEmpty())) {
LOG.info("reading filter success");
List<SyslogFilter> filters = optional.get().getSyslogFilter();
LOG.info("currently registered filters are: " + filters);
List<RegisteredSyslogFilter> registeredSyslogFiltersList = new ArrayList<>();
for (SyslogFilter filter : filters) {
LOG.info("filter entity: " + filter.getFilterEntity());
LOG.info("filter ID: " + filter.getFilterId());
RegisteredFilterEntity registeredFilterEntity = new RegisteredFilterEntityBuilder()
.setApplication(filter.getFilterEntity().getApplication())
.setContent(filter.getFilterEntity().getContent())
.setFacility(filter.getFilterEntity().getFacility())
.setHost(filter.getFilterEntity().getHost())
.setPid(filter.getFilterEntity().getPid())
.setSid(filter.getFilterEntity().getSid())
.setSeverity(filter.getFilterEntity().getSeverity())
.build();
RegisteredSyslogFilter filter1 = new RegisteredSyslogFilterBuilder()
.setFilterId(filter.getFilterId())
.setRegisteredFilterEntity(registeredFilterEntity)
.setCallbackUrl(filter.getCallbackUrl())
.build();
registeredSyslogFiltersList.add(filter1);
}
ShowRegisterFilterOutput output = new ShowRegisterFilterOutputBuilder()
.setResult("registered filters are:")
.setRegisteredSyslogFilter(registeredSyslogFiltersList)
.build();
return RpcResultBuilder.success(output).buildFuture();
} else {
ShowRegisterFilterOutput output = new ShowRegisterFilterOutputBuilder()
.setResult("no registered filter")
.build();
return RpcResultBuilder.success(output).buildFuture();
}
}
@Override
public Future<RpcResult<ConfigThreadpoolOutput>> configThreadpool(ConfigThreadpoolInput input) {
if (input.getCoreThreadNumber() != 0) {
threadPool.setCorePoolSize(input.getCoreThreadNumber());
}
if (input.getMaxThreadNumber() != 0) {
threadPool.setMaximumPoolSize(input.getMaxThreadNumber());
}
if (input.getKeepAliveTime() != 0) {
threadPool.setKeepAliveTime(input.getKeepAliveTime(), TimeUnit.SECONDS);
}
ConfigThreadpoolOutput output = new ConfigThreadpoolOutputBuilder()
.setResult("success")
.build();
return RpcResultBuilder.success(output).buildFuture();
}
@Override
public Future<RpcResult<RegisterFilterOutput>> registerFilter(RegisterFilterInput input) {
LOG.info("Received a new Register");
String url = input.getCallbackUrl();
WriteTransaction transaction = db.newWriteOnlyTransaction();
String filterID = UUID.randomUUID().toString();
String listenerUUID = UUID.randomUUID().toString();
FilterEntity filterEntity = new FilterEntityBuilder()
.setSeverity(input.getSeverity())
.setFacility(input.getFacility())
.setHost(input.getHost())
.setApplication(input.getApplication())
.setSid(input.getSid())
.setPid(input.getPid())
.setContent(input.getContent())
.build();
InstanceIdentifier<SyslogFilter> filterIID = InstanceIdentifier.create(SyslogDispatcher.class)
.child(SyslogFilter.class, new SyslogFilterKey(filterID));
SyslogFilter filter = new SyslogFilterBuilder()
.setFilterId(filterID)
.setFilterEntity(filterEntity)
.setCallbackUrl(url)
.build();
transaction.merge(LogicalDatastoreType.CONFIGURATION, filterIID, filter);
InstanceIdentifier<Listener> listenerIID =
filterIID.child(Listener.class, new ListenerKey(listenerUUID));
Listener listener = new ListenerBuilder().setListenerId(listenerUUID).build();
transaction.merge(LogicalDatastoreType.CONFIGURATION, listenerIID, listener);
//Create Listener on Operational Tree
InstanceIdentifier<SyslogListener> syslogListenerIID =
InstanceIdentifier.create(SyslogDispatcher.class)
.child(SyslogListener.class, new SyslogListenerKey(listenerUUID));
SyslogListener syslogListener =
new SyslogListenerBuilder().setListenerId(listenerUUID).setSyslogMessage("").build();
transaction.merge(LogicalDatastoreType.OPERATIONAL, syslogListenerIID, syslogListener);
try {
transaction.submit().get();
} catch (InterruptedException | ExecutionException e) {
LOG.error(e.getMessage());
}
RegisterFilterOutput output = new RegisterFilterOutputBuilder()
.setListenerId(listenerUUID).build();
RegisteredListener newRrgisteredListener = new RegisteredListener(db, listenerUUID, url);
registerMap.put(filterID, listenerUUID);
listenerMap.put(listenerUUID, newRrgisteredListener);
newRrgisteredListener.listen();
LOG.info(newRrgisteredListener.toString());
return RpcResultBuilder.success(output).buildFuture();
}
class WorkerThread implements Runnable {
private final int mid;
private final String ipaddr;
private final Message message;
public WorkerThread(int mid, String ipaddr, Message message) {
this.mid = mid;
this.ipaddr = ipaddr;
this.message = message;
}
public List<SyslogFilter> getFilters() {
if(db==null) return null;
ReadTransaction transaction = db.newReadOnlyTransaction();
InstanceIdentifier<SyslogDispatcher> iid =
InstanceIdentifier.create(SyslogDispatcher.class);
CheckedFuture<Optional<SyslogDispatcher>, ReadFailedException> future =
transaction.read(LogicalDatastoreType.CONFIGURATION, iid);
Optional<SyslogDispatcher> optional = Optional.absent();
try {
optional = future.checkedGet();
} catch (ReadFailedException e) {
LOG.warn("Reading Filter failed:", e);
return null;
}
if (optional.isPresent()) {
LOG.info("reading filter success");
return optional.get().getSyslogFilter();
} else {
return null;
}
}
private List<Listener> getListenerList(String filterID) {
if(db==null) return null;
ReadTransaction transaction = db.newReadOnlyTransaction();
InstanceIdentifier<SyslogFilter> iid = InstanceIdentifier.create(SyslogDispatcher.class)
.child(SyslogFilter.class, new SyslogFilterKey(filterID));
CheckedFuture<Optional<SyslogFilter>, ReadFailedException> future =
transaction.read(LogicalDatastoreType.CONFIGURATION, iid);
Optional<SyslogFilter> optional = Optional.absent();
try {
optional = future.checkedGet();
} catch (ReadFailedException e) {
LOG.warn("Reading Listener failed:", e);
}
if (optional.isPresent()) {
return optional.get().getListener();
} else {
return null;
}
}
private void update(List<Listener> nodes) {
if(db==null) return;
WriteTransaction transaction = db.newWriteOnlyTransaction();
InstanceIdentifier<SyslogDispatcher> baseIID = InstanceIdentifier.create(SyslogDispatcher.class);
for (Listener node : nodes) {
String listenerUUID = node.getListenerId();
InstanceIdentifier<SyslogListener> iid =
baseIID.child(SyslogListener.class, new SyslogListenerKey(listenerUUID));
SyslogListener listener = new SyslogListenerBuilder()
.setListenerId(listenerUUID)
.setSyslogMessage(message.getContent())
.build();
transaction.put(LogicalDatastoreType.OPERATIONAL, iid, listener);
}
transaction.submit();
}
@Override
public void run() {
Message msg = this.message;
List<Listener> nodes = new ArrayList<>();
if (msg != null && this.getFilters() != null) {
List<SyslogFilter> filters = this.getFilters();
for (SyslogFilter filter : filters) {
MessageFilter messageFilter = MessageFilter.FilterBuilder.create(filter.getFilterEntity());
if (messageFilter.equals(msg)) {
//Match
nodes.addAll(getListenerList(filter.getFilterId()));
}
}
}
update(nodes);
}
}
}