/** * @RDE_HEADER@ * Handler for CheckmkHandler * $Id$ * $HeadURL$ * $Author$ * Created by Norbert Thoden, 2022-11-30 */ #include "checkmk_handler.h" #include #include #include /* Laut Thore: auf deinem Rechner muss netcat (nc) installiert sein: zypper install netcat Beispiel: printf "GET hosts\nColumns: name state\nOutputFormat: json\nFilter: name ~ AHTC\nFilter: name ~ REF\n" | nc 10.198.80.58 6557 printf "GET hosts\nColumns: name state last_check\nOutputFormat: json\n\n" | nc 10.198.80.58 6557 ctc@CTCAP15:~/TDS/TDS-source-fb22_09_resmgmt2.0-clean> printf "GET hosts\nColumns: name state\nOutputFormat: json\nFilter: name ~ AHTC\n\n" | nc 10.198.80.58 6557 [["AHTC_Tetra",0], ["ENT_AHTC_CCSAWS01",0], ["ENT_AHTC_CCSDNS01",0], ["ENT_AHTC_EXCAVS01",0], ["ENT_AHTC_EXCDBS01",0], ["ENT_AHTC_EXCTDS01",1], ["RC_AHTC_CCSAWS01",0], ["RC_AHTC_CCSDNS01",0], ["RC_AHTC_EXCAVS01",0], ["RC_AHTC_EXCDBS01",0], ["RC_AHTC_EXCTDS01",0], ["REF_AHTC_CCSAWS01",0], ["REF_AHTC_CCSDNS01",0], ["REF_AHTC_EXCAVS01",1], ["REF_AHTC_EXCDBS01",1], ["REF_AHTC_EXCOWS02",0], ["REF_AHTC_EXCTDS01",1]] ctc@CTCAP15:~/TDS/TDS-source-fb22_09_resmgmt2.0-clean> // Logfile ssh rme@10.198.80.58 !Mou….. su - !Mou…. tail -f /omd/sites/Referenzanlagen/var/nagios/livestatus.log */ namespace ComponentMonitor { CheckmkHandler::CheckmkHandler() : QObject() , m_socket(new QTcpSocket(this)) , m_logger(getLogger("CheckmkHandler")) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } CheckmkHandler::~CheckmkHandler() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; if (m_timer) m_timer->stop(); delete m_socket; TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } bool CheckmkHandler::initialize(const QString &checkMkServerHostname, int checkMkServerPort, int checkIntervalSec) { DEBUG_LOG(m_logger) << LOGFUNCTION << " START" << " , hostname=" << checkMkServerHostname << " , port=" << checkMkServerPort << " , m_checkIntervalSec=" << checkIntervalSec; m_checkMkHost = checkMkServerHostname; m_checkMkPort = checkMkServerPort; m_checkIntervalSec = checkIntervalSec; if (!m_timer) { m_timer = new QTimer(this); m_timer->setInterval(m_checkIntervalSec * 1000); connect(m_timer, &QTimer::timeout, this, &CheckmkHandler::onTimeout); } connect(m_socket, &QTcpSocket::stateChanged, this, &CheckmkHandler::onStateChanged); connect(m_socket, QOverload::of(&QAbstractSocket::error), this, &CheckmkHandler::onSocketError); connect(m_socket, &QTcpSocket::connected, this, &CheckmkHandler::onConnected); connect(m_socket, &QTcpSocket::readyRead, this, &CheckmkHandler::readData); INFO_LOG(m_logger) << LOGFUNCTION << " END"; return true; } void CheckmkHandler::run() { INFO_LOG(m_logger) << LOGFUNCTION << " START"; m_timer->start(); INFO_LOG(m_logger) << LOGFUNCTION << " END"; } void CheckmkHandler::stop() { INFO_LOG(m_logger) << LOGFUNCTION << " START"; m_timer->stop(); INFO_LOG(m_logger) << LOGFUNCTION << " END"; } void CheckmkHandler::onTimeout() { if (m_highFrequencyCheck>=0) { --m_highFrequencyCheck; m_timer->setInterval(2*1000); } else { m_timer->setInterval(m_checkIntervalSec*1000); } INFO_LOG(m_logger) << LOGFUNCTION << " Timeout, m_highFrequencyCheck=" << m_highFrequencyCheck << ", current Interval=" << m_timer->interval() << " [msec]"; if (m_socket->state() == QAbstractSocket::SocketState::ConnectedState) { writeGetStateRequest(); } else { doConnect(); } } bool CheckmkHandler::doConnect() { INFO_LOG(m_logger) << LOGFUNCTION << " START, (int)m_socket->state()=" << (int)m_socket->state(); // weird, but most likely we have to connect every time after 'GET hosts\nColumns: name state last....' m_socket->connectToHost(m_checkMkHost, m_checkMkPort); if (!m_socket->waitForConnected(5000)) { ERROR_LOG(m_logger) << LOGFUNCTION << " Could not (re)open tcp destination " << m_checkMkHost << ":" << m_checkMkPort << ", error: " << m_socket->errorString(); return false; } INFO_LOG(m_logger) << LOGFUNCTION << " (Re)openend tcp destination " << m_checkMkHost << ":" << m_checkMkPort; INFO_LOG(m_logger) << LOGFUNCTION << " END"; return true; } void CheckmkHandler::onImmediateGetStateRequested() { INFO_LOG(m_logger) << LOGFUNCTION << " START"; // perform 3 'GET hosts ...' assuming the test results will be updated within that time (3*2000msec) m_highFrequencyCheck = 3; onTimeout(); INFO_LOG(m_logger) << LOGFUNCTION << " END"; } bool CheckmkHandler::writeGetStateRequest() { if (!m_socket->isOpen()) { ERROR_LOG(m_logger) << LOGFUNCTION << " socket not open"; return false; } // Doku : // lq "GET columns\nFilter: table = hosts\nColumns: name description // printf "GET columns\nFilter: table = hosts\nColumns: name description\n\n" | nc 10.198.80.58 6557 // .. // state;The current state of the host (0: up, 1: down, 2: unreachable) // .. // last_check;Time of the last check (Unix timestamp) // : Doku // printf "GET hosts\nColumns: name state\nOutputFormat: json\nFilter: name ~ AHTC\nFilter: name ~ REF\n\n" | nc 10.198.80.58 6557 // int bWritten = m_socket->write("GET hosts\nColumns: name state\nOutputFormat: json\n\n"); // int bWritten = m_socket->write("GET hosts\nColumns: name state\nOutputFormat: json\nFilter: name ~ AHTC\n\n"); // int bWritten = m_socket->write("GET hosts\nColumns: name state last_state_change\nFilter: name ~ AHTC\n\n"); // add 'KeepAlive' to command -> still getting RemoteHostClosedError // int bWritten = m_socket->write("GET hosts\nColumns: name state last_check KeepAlive on\n\n"); //int bWritten = m_socket->write("GET hosts\nColumns: name state last_check\n\n"); // use KeepAlive AND ResponseHeader to keep the connection (avoid disconnection) // "Kombinieren Sie KeepAlive immer mit dem ResponseHeader, um" // https://docs.checkmk.com/latest/de/livestatus.html#response_header const char* cmdCheck = "GET hosts\nResponseHeader: fixed16\nColumns: name state last_check \nKeepAlive: on\n\n"; int bWritten = m_socket->write(cmdCheck); INFO_LOG(m_logger) << LOGFUNCTION << " cmdCheck= '" << cmdCheck << "'"; INFO_LOG(m_logger) << LOGFUNCTION << " bWritten= " << bWritten; return true; } void CheckmkHandler::readData() { DEBUG_LOG(m_logger) << LOGFUNCTION << " XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; while (m_socket->canReadLine()) processLine(m_socket->readLine().trimmed()); } void CheckmkHandler::onConnected() { writeGetStateRequest(); } void CheckmkHandler::onStateChanged(const QAbstractSocket::SocketState &newState) { qDebug() << "CheckmkHandler: SocketState changed to: " << newState; } void CheckmkHandler::onSocketError(const QAbstractSocket::SocketError &socketError) { qCritical() << "CheckmkHandler: Error occured: " << socketError; } bool CheckmkHandler::processLine(const QByteArray &line) { // like RC_AHTC_CCSAWS01;0 if (line.isEmpty()) return false; QString str = QString::fromUtf8(line); QStringList sl = str.split(QStringLiteral(";")); if (sl.size() < 2) { if (str.startsWith(QStringLiteral("200"))) DEBUG_LOG(m_logger) << LOGFUNCTION << " size to small " << sl.size() << ", line='" << str << "'"; else WARNING_LOG(m_logger) << LOGFUNCTION << " size to small " << sl.size() << ", line='" << str << "'"; return false; } QString name = sl.value(0); name = name.section(QLatin1Char('.'), 0, 0); // ahtcENT.defence-elec.de -> ahtcENT //#ifdef LTS_DEBUG // // Just for Debugging purpose (server at 10.198.80.58 6557) // if (name.compare(QStringLiteral("REF_GUEZ_ECVMADB01")) == 0) // name = QStringLiteral("UBEBSM0001"); // //#endif // LTS_DEBUG // state;The current state of the host (0: up, 1: down, 2: unreachable) // state;The current state of the host (0: up, 1: down, 2: unreachable) // state;The current state of the host (0: up, 1: down, 2: unreachable) QString stateStr = sl.value(1); int stateInt = stateStr.toInt(); bool reachable = stateInt == 0; long long lastCheck = 0; if (sl.size() >= 3) { QString lastCheckStr = sl.value(2); lastCheck = lastCheckStr.toLongLong(); } DEBUG_LOG(m_logger) << LOGFUNCTION << " name: '" << name << "'" << ", stateInt: '" << stateInt << "'" << ", reachable: '" << reachable << "'" << ", lastCheck: '" << lastCheck << "'" ; emit newState(name, reachable, lastCheck); return true; } } // namespace ComponentMonitor