Update to version 3.2
This commit is contained in:
@@ -20,19 +20,17 @@
|
||||
#include <gempa/caps/log.h>
|
||||
#include <gempa/caps/utils.h>
|
||||
|
||||
#include <seiscomp3/core/system.h>
|
||||
#include <seiscomp3/logging/log.h>
|
||||
#include <seiscomp/core/system.h>
|
||||
#include <seiscomp/logging/log.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#ifdef SC_GEMPA_SEATTLE
|
||||
#include <seiscomp3/system/environment.h>
|
||||
#else
|
||||
#include <seiscomp3/config/environment.h>
|
||||
#endif
|
||||
#include <seiscomp/system/environment.h>
|
||||
|
||||
using namespace std;
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
namespace sc = Seiscomp::Core;
|
||||
|
||||
namespace {
|
||||
@@ -73,52 +71,91 @@ void LogDebug(const char *fmt, ...) {
|
||||
}
|
||||
|
||||
const size_t MIN_BUFFER_SIZE = 1024*16;
|
||||
const uint16_t DEFAULT_PORT = 18003;
|
||||
|
||||
bool readKeyValueFile(std::map<std::string, std::string> &data,
|
||||
const std::string &filename, const char *sep) {
|
||||
std::ifstream ifs(filename.c_str());
|
||||
if ( !ifs.is_open() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
while ( getline(ifs, line) ) {
|
||||
const char* str = line.c_str();
|
||||
int len = strlen(str);
|
||||
const char *tok = nullptr;
|
||||
int tok_len = 0;
|
||||
int tok_count = 0;
|
||||
|
||||
std::string key;
|
||||
|
||||
for ( ; (tok = Gempa::CAPS::tokenize(str, sep, len, tok_len)) != nullptr;
|
||||
++tok_count ) {
|
||||
Gempa::CAPS::trim(tok, tok_len);
|
||||
if ( tok_count == 0 ) {
|
||||
if ( len == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
key.assign(tok, tok_len);
|
||||
}
|
||||
else if ( tok_count == 1 ) {
|
||||
data.insert({key, std::string(tok, tok_len)});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int64_t getAvailableMemory() {
|
||||
std::map<std::string, std::string> data;
|
||||
if ( !readKeyValueFile(data, "/proc/meminfo", ":") ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto it = data.find("MemAvailable");
|
||||
if ( it == data.end() ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::vector<std::string> tokens;
|
||||
sc::split(tokens, it->second.c_str(), "kB");
|
||||
if ( tokens.empty() ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
sc::trim(tokens[0]);
|
||||
int64_t availableMemory = 0;
|
||||
sc::fromString(availableMemory, tokens[0]);
|
||||
|
||||
return availableMemory;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
namespace Gempa::CAPS {
|
||||
|
||||
PluginApplication::PluginApplication(int argc, char **argv, const string &desc)
|
||||
PluginApplication::PluginApplication(int argc, char **argv, const std::string &name)
|
||||
: Seiscomp::Client::StreamApplication(argc, argv)
|
||||
, _plugin(Plugin(desc)) {
|
||||
_bufferSize = 1 << 20;
|
||||
_backfillingBufferSize = 180;
|
||||
_flushInterval = 10;
|
||||
_ackTimeout = 60;
|
||||
_lastAckTimeout = 5;
|
||||
_sendTimeout = 60;
|
||||
_logStatus = false;
|
||||
_statusFlushInterval = 10;
|
||||
_strAddr = "localhost:" + sc::toString(DEFAULT_PORT);
|
||||
, _plugin(Plugin(name)) {
|
||||
|
||||
SC_FS_DECLARE_PATH(path, "@ROOTDIR@/var/run/" + SCCoreApp->name() + "/journal")
|
||||
_journalFile = path.string();
|
||||
|
||||
_mseedEnabled = false;
|
||||
_mseedEncoding = Steim2;
|
||||
_mseedRecordLength = 9;
|
||||
_strMseedEncoding = "Steim2";
|
||||
_maxFutureEndTime = 120;
|
||||
_dumpPackets = false;
|
||||
fs::path path("@ROOTDIR@/var/run/" + SCCoreApp->name());
|
||||
_journalFile = (path / "journal").string();
|
||||
|
||||
// By default we disable the acquisition autostart because not all plugins
|
||||
// require this feature. It must be enabled explicitly if required.
|
||||
setAutoAcquisitionStart(false);
|
||||
setRecordStreamEnabled(true);
|
||||
}
|
||||
|
||||
void PluginApplication::createCommandLineDescription() {
|
||||
Seiscomp::Client::StreamApplication::createCommandLineDescription();
|
||||
commandline().addGroup("Output");
|
||||
commandline().addOption("Output", "addr,a",
|
||||
"Data output address\n"
|
||||
commandline().addOption("Output", "output,O",
|
||||
"Data output address. Format:\n"
|
||||
"[[caps|capss]://][user:pass@]host[:port]", &_strAddr);
|
||||
commandline().addOption("Output", "agent",
|
||||
"Sets the agent string. Allows "
|
||||
"the server to identify the "
|
||||
"application that sends data.",
|
||||
&_agent);
|
||||
commandline().addOption("Output", "buffer-size,b",
|
||||
"Size (bytes) of the packet buffer", &_bufferSize);
|
||||
commandline().addOption("Output", "backfilling",
|
||||
@@ -133,9 +170,11 @@ void PluginApplication::createCommandLineDescription() {
|
||||
commandline().addOption("Output", "rec-len", "MiniSEED record length expressed as a power of 2."
|
||||
"A 512 byte record would be 9.",
|
||||
&_mseedRecordLength);
|
||||
commandline().addOption("Output", "max-future-endtime", "Maximum allowed relative end time for packets. If "
|
||||
commandline().addOption("Output", "max-future-endtime",
|
||||
"Maximum allowed relative end time for packets. If "
|
||||
"the packet end time is greater than the current time plus this "
|
||||
"value the packet will be discarded. By default this value is set to 120 seconds.",
|
||||
"value the packet will be discarded. By default this value is set to 120 seconds. "
|
||||
"A negative value disables the check.",
|
||||
&_maxFutureEndTime);
|
||||
commandline().addOption("Output", "dump-packets", "Dump packets to stdout");
|
||||
commandline().addGroup("Journal");
|
||||
@@ -154,6 +193,14 @@ void PluginApplication::createCommandLineDescription() {
|
||||
commandline().addOption("Status", "status-flush", "Flush status every n "
|
||||
"seconds to disk",
|
||||
&_statusFlushInterval);
|
||||
|
||||
commandline().addGroup("Host");
|
||||
commandline().addOption("Host", "host-storage",
|
||||
"Determine disc capacity and available space from this path",
|
||||
&_host.storage);
|
||||
commandline().addOption("Host", "host-os",
|
||||
"Set host operating system information",
|
||||
&_host.os);
|
||||
}
|
||||
|
||||
void PluginApplication::done() {
|
||||
@@ -169,6 +216,8 @@ void PluginApplication::done() {
|
||||
_stats.endTime.valid()?_stats.endTime.iso().c_str():"",
|
||||
_stats.files);
|
||||
|
||||
_plugin.close();
|
||||
|
||||
Seiscomp::Client::StreamApplication::done();
|
||||
}
|
||||
|
||||
@@ -179,17 +228,29 @@ void PluginApplication::exit(int returnCode) {
|
||||
}
|
||||
|
||||
void PluginApplication::handleTimeout() {
|
||||
sc::Time time = sc::Time::GMT().toLocalTime();
|
||||
auto time = Time::GMT();
|
||||
auto seconds = time.seconds();
|
||||
|
||||
Plugin::Stats stats = _plugin.stats();
|
||||
_statusFile.stream() << time.toString("%Y/%m/%d %T") << " " << stats.maxBytesBuffered << endl;
|
||||
if ( _logStatus && (seconds % _statusFlushInterval == 0) ) {
|
||||
Plugin::Stats stats = _plugin.stats();
|
||||
_statusFile.stream() << time.toLocalTime().toString("%Y/%m/%d %T") << " "
|
||||
<< stats.maxBytesBuffered << std::endl;
|
||||
_plugin.resetMaxBytesBuffered();
|
||||
}
|
||||
|
||||
if ( seconds % 3 == 0 ) {
|
||||
updateRuntimeInfo();
|
||||
}
|
||||
|
||||
_plugin.resetMaxBytesBuffered();
|
||||
if ( seconds % 10 == 0 ) {
|
||||
sendRuntimeInfo();
|
||||
}
|
||||
}
|
||||
|
||||
bool PluginApplication::init() {
|
||||
if ( !Seiscomp::Client::StreamApplication::init() ) return false;
|
||||
if ( !Seiscomp::Client::StreamApplication::init() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Setup log handlers
|
||||
Gempa::CAPS::SetLogHandler(Gempa::CAPS::LL_ERROR, LogError);
|
||||
@@ -198,12 +259,31 @@ bool PluginApplication::init() {
|
||||
Gempa::CAPS::SetLogHandler(Gempa::CAPS::LL_INFO, LogInfo);
|
||||
Gempa::CAPS::SetLogHandler(Gempa::CAPS::LL_DEBUG, LogDebug);
|
||||
|
||||
Plugin::HostInfo hostInfo;
|
||||
if ( !getHostInfo(hostInfo) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_plugin.setBufferSize(_bufferSize);
|
||||
_plugin.setFlushInterval(_flushInterval);
|
||||
_plugin.setConnectionTimeout(_connectionTimeout);
|
||||
_plugin.setTimeouts(_ackTimeout, _lastAckTimeout, _sendTimeout);
|
||||
_plugin.setMaxFutureEndTime(_maxFutureEndTime);
|
||||
_plugin.dumpPackets(_dumpPackets);
|
||||
_plugin.setAgent(_agent);
|
||||
_plugin.setHostInfo(hostInfo);
|
||||
|
||||
LogInfo("CAPS connection settings\n"
|
||||
" Output CAPS server : %s\n"
|
||||
" Buffer size : %zu bytes\n"
|
||||
" Backfilling buffer size : %zu s\n"
|
||||
" Max future end time : %d s\n"
|
||||
" Connection timeout : %d s\n"
|
||||
" Timeouts Ack/LastAck/Send : %d s/%d s/%d s\n"
|
||||
" Agent : %s\n"
|
||||
" Agent version : %s",
|
||||
_strAddr.c_str(), _bufferSize, _backfillingBufferSize, _maxFutureEndTime,
|
||||
_connectionTimeout, _ackTimeout, _lastAckTimeout, _sendTimeout,
|
||||
hostInfo.agent.data(), hostInfo.agentVersion.data());
|
||||
|
||||
if ( _mseedEnabled ) {
|
||||
MSEEDEncoderFactory *factory = nullptr;
|
||||
@@ -238,7 +318,13 @@ bool PluginApplication::init() {
|
||||
|
||||
if ( _backfillingBufferSize > 0 ) {
|
||||
_plugin.setBackfillingBufferSize(_backfillingBufferSize);
|
||||
LogInfo("Backfilling buffer size set to %d", _backfillingBufferSize);
|
||||
}
|
||||
|
||||
std::string connectionIDFile = "@ROOTDIR@/var/run/" + name() + "/id";
|
||||
connectionIDFile = Seiscomp::Environment::Instance()->absolutePath(connectionIDFile);
|
||||
LogInfo("Reading connection ID from %s", connectionIDFile.c_str());
|
||||
if ( !_plugin.setConnectionIDFile(connectionIDFile) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !_journalFile.empty() ) {
|
||||
@@ -251,20 +337,25 @@ bool PluginApplication::init() {
|
||||
}
|
||||
|
||||
if ( _logStatus ) {
|
||||
string filename = Seiscomp::Environment::Instance()->logDir() + "/" + SCCoreApp->name() + "-stats.log";
|
||||
std::string filename = Seiscomp::Environment::Instance()->logDir() + "/" + SCCoreApp->name() + "-stats.log";
|
||||
if ( !_statusFile.open(filename.c_str()) ) {
|
||||
LogError("Could not open status file %s.", filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
enableTimer(_statusFlushInterval);
|
||||
}
|
||||
|
||||
// This causes a connect
|
||||
updateRuntimeInfo();
|
||||
_plugin.setRuntimeInfo(_runtimeInfo);
|
||||
enableTimer(1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PluginApplication::initConfiguration() {
|
||||
if ( !Seiscomp::Client::StreamApplication::initConfiguration() ) return false;
|
||||
if ( !Seiscomp::Client::StreamApplication::initConfiguration() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
_plugin.setHost(configGetString("output.host"));
|
||||
@@ -280,8 +371,11 @@ bool PluginApplication::initConfiguration() {
|
||||
try { _sendTimeout = configGetInt("output.timeout"); }
|
||||
catch ( ... ) { }
|
||||
|
||||
try { _connectionTimeout = configGetInt("output.connectionTimeout"); }
|
||||
catch ( ... ) { }
|
||||
|
||||
try {
|
||||
string addr = configGetString("output.address");
|
||||
std::string addr = configGetString("output.address");
|
||||
if ( !_plugin.setAddress(addr) ) {
|
||||
return false;
|
||||
}
|
||||
@@ -305,8 +399,10 @@ bool PluginApplication::initConfiguration() {
|
||||
catch ( ... ) {}
|
||||
|
||||
try {
|
||||
string str = configGetString("output.mseed.encoding");
|
||||
if ( !fromString(_mseedEncoding, str)) return false;
|
||||
std::string str = configGetString("output.mseed.encoding");
|
||||
if ( !fromString(_mseedEncoding, str)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch ( ... ) {}
|
||||
|
||||
@@ -319,16 +415,6 @@ bool PluginApplication::initConfiguration() {
|
||||
try { _maxFutureEndTime = configGetInt("output.maxFutureEndTime"); }
|
||||
catch ( ... ) { }
|
||||
|
||||
try { _agent = configGetString("output.agent"); }
|
||||
catch ( ... ) {
|
||||
if ( version() ) {
|
||||
_agent = name() + string(" ") + version();
|
||||
}
|
||||
else {
|
||||
_agent = name();
|
||||
}
|
||||
}
|
||||
|
||||
try { _journalFile = configGetString("journal.file"); }
|
||||
catch ( ... ) {}
|
||||
|
||||
@@ -341,6 +427,14 @@ bool PluginApplication::initConfiguration() {
|
||||
try { _lastAckTimeout = configGetInt("journal.waitForLastAck"); }
|
||||
catch ( ... ) { }
|
||||
|
||||
_host.agent = name();
|
||||
try { _host.storage = configGetString("host.storage"); }
|
||||
catch ( ... ) { }
|
||||
|
||||
try { _host.os = configGetString("host.os"); }
|
||||
catch ( ... ) { }
|
||||
|
||||
|
||||
try { _logStatus = configGetBool("statusLog.enable"); }
|
||||
catch ( ... ) { }
|
||||
|
||||
@@ -351,7 +445,9 @@ bool PluginApplication::initConfiguration() {
|
||||
}
|
||||
|
||||
bool PluginApplication::validateParameters() {
|
||||
if ( !Seiscomp::Client::StreamApplication::validateParameters() ) return false;
|
||||
if ( !Seiscomp::Client::StreamApplication::validateParameters() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( commandline().hasOption("mseed") ) {
|
||||
_mseedEnabled = true;
|
||||
@@ -366,7 +462,9 @@ bool PluginApplication::validateParameters() {
|
||||
}
|
||||
|
||||
if ( commandline().hasOption("encoding") ) {
|
||||
if ( !fromString(_mseedEncoding, _strMseedEncoding)) return false;
|
||||
if ( !fromString(_mseedEncoding, _strMseedEncoding)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( _bufferSize < MIN_BUFFER_SIZE ) {
|
||||
@@ -375,23 +473,28 @@ bool PluginApplication::validateParameters() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( commandline().hasOption("addr") ) {
|
||||
if ( !_plugin.setAddress(_strAddr, DEFAULT_PORT) ) {
|
||||
if ( commandline().hasOption("output") ) {
|
||||
if ( !_plugin.setAddress(_strAddr, 18003) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_host.storage = Seiscomp::Environment::Instance()->absolutePath(_host.storage);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PluginApplication::fromString(MseedEncoding &enc, string str) {
|
||||
bool PluginApplication::fromString(MseedEncoding &enc, std::string str) {
|
||||
boost::to_lower(str);
|
||||
if( str == "uncompressed" )
|
||||
if( str == "uncompressed" ) {
|
||||
enc = Uncompressed;
|
||||
else if ( str == "steim1" )
|
||||
}
|
||||
else if ( str == "steim1" ) {
|
||||
enc = Steim1;
|
||||
else if ( str == "steim2" )
|
||||
}
|
||||
else if ( str == "steim2" ) {
|
||||
enc = Steim2;
|
||||
}
|
||||
else {
|
||||
SEISCOMP_ERROR("Unsupported encoding %s", str.c_str());
|
||||
return false;
|
||||
@@ -400,5 +503,60 @@ bool PluginApplication::fromString(MseedEncoding &enc, string str) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PluginApplication::getHostInfo(Plugin::HostInfo &hostInfo) {
|
||||
hostInfo.agent = _host.agent;
|
||||
hostInfo.agentVersion = version() ? version() : "";
|
||||
hostInfo.totalMem = _hostInfo.totalMemory();
|
||||
|
||||
try {
|
||||
auto spaceInfo = fs::space(_host.storage);
|
||||
hostInfo.totalDisc = spaceInfo.capacity / 1024;
|
||||
}
|
||||
catch ( const fs::filesystem_error& e ) {
|
||||
SEISCOMP_ERROR("Failed to determine filesystem information: %s", e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
hostInfo.os = _host.os;
|
||||
if ( hostInfo.os.empty() ) {
|
||||
std::map<std::string, std::string> osRelease;
|
||||
if ( !readKeyValueFile(osRelease, "/etc/os-release", "=") ) {
|
||||
SEISCOMP_WARNING("Unable to read file /etc/os-release");
|
||||
}
|
||||
|
||||
auto it = osRelease.find("PRETTY_NAME");
|
||||
if ( it != osRelease.end() ) {
|
||||
auto unquote = [](const std::string& str) {
|
||||
if ( str.length() >= 2 && str.front() == '"' && str.back() == '"' ) {
|
||||
return str.substr(1, str.length() - 2);
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
hostInfo.os = unquote(it->second);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PluginApplication::updateRuntimeInfo() {
|
||||
_runtimeInfo.cpuUsage = std::max(0, static_cast<int>(_hostInfo.getCurrentCpuUsage() * 1000));
|
||||
_runtimeInfo.procUsedMem = _hostInfo.getCurrentMemoryUsage();
|
||||
_runtimeInfo.availableMem = getAvailableMemory();
|
||||
getloadavg(&_runtimeInfo.systemLoad, 1);
|
||||
|
||||
try {
|
||||
auto spaceInfo = fs::space(_host.storage);
|
||||
_runtimeInfo.availableDisc = spaceInfo.available / 1024;
|
||||
}
|
||||
catch ( const fs::filesystem_error& e ) {
|
||||
SEISCOMP_WARNING("Failed to determine filesystem information: %s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void PluginApplication::sendRuntimeInfo() {
|
||||
_plugin.setRuntimeInfo(_runtimeInfo);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user