From d7efa9e24c4ed608857a34d3f4174bd95e6bcb33 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Sun, 10 Mar 2013 09:23:13 +0100 Subject: [PATCH] Implement basic livestatus GET queries. --- components/livestatus/Makefile.am | 20 ++-- ...{livestatuscomponent.cpp => component.cpp} | 1 + .../{livestatuscomponent.h => component.h} | 2 +- ...ivestatusconnection.cpp => connection.cpp} | 3 +- .../{livestatusconnection.h => connection.h} | 2 +- components/livestatus/filter.cpp | 26 +++++ components/livestatus/filter.h | 43 ++++++++ components/livestatus/i2-livestatus.h | 12 +- components/livestatus/livestatus.vcxproj | 20 ++-- .../livestatus/livestatus.vcxproj.filters | 28 +++-- components/livestatus/livestatustable.cpp | 0 components/livestatus/livestatustable.h | 0 .../{livestatusquery.cpp => query.cpp} | 104 ++++++++++++++++-- .../livestatus/{livestatusquery.h => query.h} | 18 +-- components/livestatus/statustable.cpp | 99 +++++++++++++++++ components/livestatus/statustable.h | 45 ++++++++ components/livestatus/table.cpp | 81 ++++++++++++++ 17 files changed, 455 insertions(+), 49 deletions(-) rename components/livestatus/{livestatuscomponent.cpp => component.cpp} (99%) rename components/livestatus/{livestatuscomponent.h => component.h} (98%) rename components/livestatus/{livestatusconnection.cpp => connection.cpp} (96%) rename components/livestatus/{livestatusconnection.h => connection.h} (98%) create mode 100644 components/livestatus/filter.cpp create mode 100644 components/livestatus/filter.h delete mode 100644 components/livestatus/livestatustable.cpp delete mode 100644 components/livestatus/livestatustable.h rename components/livestatus/{livestatusquery.cpp => query.cpp} (60%) rename components/livestatus/{livestatusquery.h => query.h} (88%) create mode 100644 components/livestatus/statustable.cpp create mode 100644 components/livestatus/statustable.h create mode 100644 components/livestatus/table.cpp diff --git a/components/livestatus/Makefile.am b/components/livestatus/Makefile.am index b1745b604..3ae7e0519 100644 --- a/components/livestatus/Makefile.am +++ b/components/livestatus/Makefile.am @@ -4,14 +4,18 @@ pkglib_LTLIBRARIES = \ livestatus.la livestatus_la_SOURCES = \ - livestatuscomponent.cpp \ - livestatuscomponent.h \ - livestatusconnection.cpp \ - livestatusconnection.h \ - livestatusquery.cpp \ - livestatusquery.h \ - livestatustable.cpp \ - livestatustable.h \ + component.cpp \ + component.h \ + connection.cpp \ + connection.h \ + filter.cpp \ + filter.h \ + query.cpp \ + query.h \ + statustable.cpp \ + statustable.h \ + table.cpp \ + table.h \ i2-livestatus.h livestatus_la_CPPFLAGS = \ diff --git a/components/livestatus/livestatuscomponent.cpp b/components/livestatus/component.cpp similarity index 99% rename from components/livestatus/livestatuscomponent.cpp rename to components/livestatus/component.cpp index 5604e0b72..892c32a40 100644 --- a/components/livestatus/livestatuscomponent.cpp +++ b/components/livestatus/component.cpp @@ -20,6 +20,7 @@ #include "i2-livestatus.h" using namespace icinga; +using namespace livestatus; REGISTER_COMPONENT("livestatus", LivestatusComponent); diff --git a/components/livestatus/livestatuscomponent.h b/components/livestatus/component.h similarity index 98% rename from components/livestatus/livestatuscomponent.h rename to components/livestatus/component.h index ca79f356e..5a1aec784 100644 --- a/components/livestatus/livestatuscomponent.h +++ b/components/livestatus/component.h @@ -20,7 +20,7 @@ #ifndef LIVESTATUSCOMPONENT_H #define LIVESTATUSCOMPONENT_H -namespace icinga +namespace livestatus { /** diff --git a/components/livestatus/livestatusconnection.cpp b/components/livestatus/connection.cpp similarity index 96% rename from components/livestatus/livestatusconnection.cpp rename to components/livestatus/connection.cpp index 29bc50240..ceca93cfb 100644 --- a/components/livestatus/livestatusconnection.cpp +++ b/components/livestatus/connection.cpp @@ -20,6 +20,7 @@ #include "i2-livestatus.h" using namespace icinga; +using namespace livestatus; LivestatusConnection::LivestatusConnection(const Stream::Ptr& stream) : Connection(stream) @@ -47,7 +48,7 @@ void LivestatusConnection::ProcessData(void) if (line.GetLength() > 0 && !GetStream()->IsReadEOF()) return; - LivestatusQuery::Ptr query = boost::make_shared(m_Lines); + Query::Ptr query = boost::make_shared(m_Lines); m_Lines.clear(); query->Execute(GetStream()); diff --git a/components/livestatus/livestatusconnection.h b/components/livestatus/connection.h similarity index 98% rename from components/livestatus/livestatusconnection.h rename to components/livestatus/connection.h index 52e014402..029b4f0fa 100644 --- a/components/livestatus/livestatusconnection.h +++ b/components/livestatus/connection.h @@ -20,7 +20,7 @@ #ifndef LIVESTATUSCONNECTION_H #define LIVESTATUSCONNECTION_H -namespace icinga +namespace livestatus { class LivestatusConnection : public Connection diff --git a/components/livestatus/filter.cpp b/components/livestatus/filter.cpp new file mode 100644 index 000000000..bbf262f97 --- /dev/null +++ b/components/livestatus/filter.cpp @@ -0,0 +1,26 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012 Icinga Development Team (http://www.icinga.org/) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#include "i2-livestatus.h" + +using namespace icinga; +using namespace livestatus; + +Filter::Filter(void) +{ } diff --git a/components/livestatus/filter.h b/components/livestatus/filter.h new file mode 100644 index 000000000..014653421 --- /dev/null +++ b/components/livestatus/filter.h @@ -0,0 +1,43 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012 Icinga Development Team (http://www.icinga.org/) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#ifndef FILTER_H +#define FILTER_H + +namespace livestatus +{ + +/** + * @ingroup livestatus + */ +class Filter : public Object +{ +public: + typedef shared_ptr Ptr; + typedef weak_ptr WeakPtr; + + virtual bool Apply(const Object::Ptr& object) = 0; + +protected: + Filter(void); +}; + +} + +#endif /* FILTER_H */ diff --git a/components/livestatus/i2-livestatus.h b/components/livestatus/i2-livestatus.h index a94e818d6..1d2904fd4 100644 --- a/components/livestatus/i2-livestatus.h +++ b/components/livestatus/i2-livestatus.h @@ -30,9 +30,13 @@ #include #include -#include "livestatusconnection.h" -#include "livestatusquery.h" -#include "livestatustable.h" -#include "livestatuscomponent.h" +using namespace icinga; + +#include "connection.h" +#include "query.h" +#include "filter.h" +#include "table.h" +#include "statustable.h" +#include "component.h" #endif /* I2LIVESTATUS_H */ diff --git a/components/livestatus/livestatus.vcxproj b/components/livestatus/livestatus.vcxproj index e7ef4b628..e330d7fb9 100644 --- a/components/livestatus/livestatus.vcxproj +++ b/components/livestatus/livestatus.vcxproj @@ -19,17 +19,21 @@ + - - - - + + + + + - - - - + + + + + + {950E8743-BB34-4F8A-99EC-C87E8FC0EB3F} diff --git a/components/livestatus/livestatus.vcxproj.filters b/components/livestatus/livestatus.vcxproj.filters index 533492f32..732473d02 100644 --- a/components/livestatus/livestatus.vcxproj.filters +++ b/components/livestatus/livestatus.vcxproj.filters @@ -12,30 +12,42 @@ Headerdateien - + Headerdateien - + Headerdateien - + Headerdateien - + + Headerdateien + + + Headerdateien + + Headerdateien - + + Quelldateien + + + Quelldateien + + Quelldateien - + Quelldateien - + Quelldateien - + Quelldateien diff --git a/components/livestatus/livestatustable.cpp b/components/livestatus/livestatustable.cpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/components/livestatus/livestatustable.h b/components/livestatus/livestatustable.h deleted file mode 100644 index e69de29bb..000000000 diff --git a/components/livestatus/livestatusquery.cpp b/components/livestatus/query.cpp similarity index 60% rename from components/livestatus/livestatusquery.cpp rename to components/livestatus/query.cpp index 8e89a6582..70ffa964f 100644 --- a/components/livestatus/livestatusquery.cpp +++ b/components/livestatus/query.cpp @@ -20,11 +20,10 @@ #include "i2-livestatus.h" using namespace icinga; +using namespace livestatus; - - -LivestatusQuery::LivestatusQuery(const vector& lines) - : m_KeepAlive(false), m_ColumnHeaders(true), m_Limit(-1) +Query::Query(const vector& lines) + : m_OutputFormat("csv"), m_KeepAlive(false), m_ColumnHeaders(true), m_Limit(-1) { String line = lines[0]; @@ -55,15 +54,100 @@ LivestatusQuery::LivestatusQuery(const vector& lines) if (header == "ResponseHeader") m_ResponseHeader = params; + else if (header == "OutputFormat") + m_OutputFormat = params; + else if (header == "Columns") + m_Columns = params.Split(is_any_of(" ")); + } +} + +void Query::PrintResultSet(ostream& fp, const vector& columns, const Array::Ptr& rs) +{ + if (m_OutputFormat == "csv" && m_Columns.size() == 0) { + bool first = true; + + BOOST_FOREACH(const String& column, columns) { + if (first) + first = false; + else + fp << ";"; + + fp << column; + } + + fp << "\n"; + } + + if (m_OutputFormat == "csv") { + ObjectLock olock(rs); + + BOOST_FOREACH(const Array::Ptr& row, rs) { + bool first = true; + + ObjectLock rlock(row); + BOOST_FOREACH(const Value& value, row) { + if (first) + first = false; + else + fp << ";"; + + fp << Convert::ToString(value); + } + + fp << "\n"; + } + } else if (m_OutputFormat == "json") { + fp << Value(rs).Serialize(); } } -void LivestatusQuery::ExecuteGetHelper(const Stream::Ptr& stream) +void Query::ExecuteGetHelper(const Stream::Ptr& stream) { Logger::Write(LogInformation, "livestatus", "Table: " + m_Table); + + Table::Ptr table = Table::GetByName(m_Table); + + if (!table) { + SendResponse(stream, 404, "Table '" + m_Table + "' does not exist."); + + return; + } + + vector objects = table->FilterRows(Filter::Ptr()); + vector columns; + + if (m_Columns.size() > 0) + columns = m_Columns; + else + columns = table->GetColumnNames(); + + Array::Ptr rs = boost::make_shared(); + + BOOST_FOREACH(const Object::Ptr& object, objects) { + Array::Ptr row = boost::make_shared(); + + BOOST_FOREACH(const String& column, columns) { + Table::ColumnAccessor accessor = table->GetColumn(column); + + if (accessor.empty()) { + SendResponse(stream, 450, "Column '" + column + "' does not exist."); + + return; + } + + row->Add(accessor(object)); + } + + rs->Add(row); + } + + stringstream result; + PrintResultSet(result, columns, rs); + + SendResponse(stream, 200, result.str()); } -void LivestatusQuery::ExecuteCommandHelper(const Stream::Ptr& stream) +void Query::ExecuteCommandHelper(const Stream::Ptr& stream) { try { Logger::Write(LogInformation, "livestatus", "Executing command: " + m_Command); @@ -74,12 +158,12 @@ void LivestatusQuery::ExecuteCommandHelper(const Stream::Ptr& stream) } } -void LivestatusQuery::ExecuteErrorHelper(const Stream::Ptr& stream) +void Query::ExecuteErrorHelper(const Stream::Ptr& stream) { PrintFixed16(stream, m_ErrorCode, m_ErrorMessage); } -void LivestatusQuery::SendResponse(const Stream::Ptr& stream, int code, const String& data) +void Query::SendResponse(const Stream::Ptr& stream, int code, const String& data) { if (m_ResponseHeader == "fixed16") PrintFixed16(stream, code, data); @@ -88,7 +172,7 @@ void LivestatusQuery::SendResponse(const Stream::Ptr& stream, int code, const St stream->Write(data.CStr(), data.GetLength()); } -void LivestatusQuery::PrintFixed16(const Stream::Ptr& stream, int code, const String& data) +void Query::PrintFixed16(const Stream::Ptr& stream, int code, const String& data) { ASSERT(code >= 100 && code <= 999); @@ -99,7 +183,7 @@ void LivestatusQuery::PrintFixed16(const Stream::Ptr& stream, int code, const St stream->Write(header.CStr(), header.GetLength()); } -void LivestatusQuery::Execute(const Stream::Ptr& stream) +void Query::Execute(const Stream::Ptr& stream) { Logger::Write(LogInformation, "livestatus", "Executing livestatus query: " + m_Verb); diff --git a/components/livestatus/livestatusquery.h b/components/livestatus/query.h similarity index 88% rename from components/livestatus/livestatusquery.h rename to components/livestatus/query.h index 22ee1c4b8..940a80884 100644 --- a/components/livestatus/livestatusquery.h +++ b/components/livestatus/query.h @@ -17,22 +17,22 @@ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * ******************************************************************************/ -#ifndef LIVESTATUSQUERY_H -#define LIVESTATUSQUERY_H +#ifndef QUERY_H +#define QUERY_H -namespace icinga +namespace livestatus { /** * @ingroup livestatus */ -class LivestatusQuery : public Object +class Query : public Object { public: - typedef shared_ptr Ptr; - typedef weak_ptr WeakPtr; + typedef shared_ptr Ptr; + typedef weak_ptr WeakPtr; - LivestatusQuery(const vector& lines); + Query(const vector& lines); void Execute(const Stream::Ptr& stream); @@ -58,6 +58,8 @@ private: int m_ErrorCode; String m_ErrorMessage; + void PrintResultSet(ostream& fp, const vector& columns, const Array::Ptr& rs); + void ExecuteGetHelper(const Stream::Ptr& stream); void ExecuteCommandHelper(const Stream::Ptr& stream); void ExecuteErrorHelper(const Stream::Ptr& stream); @@ -68,4 +70,4 @@ private: } -#endif /* LIVESTATUSQUERY_H */ +#endif /* QUERY_H */ diff --git a/components/livestatus/statustable.cpp b/components/livestatus/statustable.cpp new file mode 100644 index 000000000..41cdc9a2a --- /dev/null +++ b/components/livestatus/statustable.cpp @@ -0,0 +1,99 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012 Icinga Development Team (http://www.icinga.org/) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#include "i2-livestatus.h" + +using namespace icinga; +using namespace livestatus; + +StatusTable::StatusTable(void) +{ + AddColumn("neb_callbacks", &Table::ZeroAccessor); + AddColumn("neb_callbacks_rate", &Table::ZeroAccessor); + + AddColumn("requests", &Table::ZeroAccessor); + AddColumn("requests_rate", &Table::ZeroAccessor); + + AddColumn("connections", &Table::ZeroAccessor); + AddColumn("connections_rate", &Table::ZeroAccessor); + + AddColumn("service_checks", &Table::ZeroAccessor); + AddColumn("service_checks_rate", &Table::ZeroAccessor); + + AddColumn("host_checks", &Table::ZeroAccessor); + AddColumn("host_checks_rate", &Table::ZeroAccessor); + + AddColumn("forks", &Table::ZeroAccessor); + AddColumn("forks_rate", &Table::ZeroAccessor); + + AddColumn("log_messages", &Table::ZeroAccessor); + AddColumn("log_messages_rate", &Table::ZeroAccessor); + + AddColumn("external_commands", &Table::ZeroAccessor); + AddColumn("external_commands_rate", &Table::ZeroAccessor); + + AddColumn("livechecks", &Table::ZeroAccessor); + AddColumn("livechecks_rate", &Table::ZeroAccessor); + + AddColumn("livecheck_overflows", &Table::ZeroAccessor); + AddColumn("livecheck_overflows_rate", &Table::ZeroAccessor); + + AddColumn("nagios_pid", &Table::ZeroAccessor); + AddColumn("enable_notifications", &Table::ZeroAccessor); + AddColumn("execute_service_checks", &Table::ZeroAccessor); + AddColumn("accept_passive_service_checks", &Table::ZeroAccessor); + AddColumn("execute_host_checks", &Table::ZeroAccessor); + AddColumn("accept_passive_host_checks", &Table::ZeroAccessor); + AddColumn("enable_event_handlers", &Table::ZeroAccessor); + AddColumn("obsess_over_services", &Table::ZeroAccessor); + AddColumn("obsess_over_hosts", &Table::ZeroAccessor); + AddColumn("check_service_freshness", &Table::ZeroAccessor); + AddColumn("check_host_freshness", &Table::ZeroAccessor); + AddColumn("enable_flap_detection", &Table::ZeroAccessor); + AddColumn("process_performance_data", &Table::ZeroAccessor); + AddColumn("check_external_commands", &Table::ZeroAccessor); + AddColumn("program_start", &Table::ZeroAccessor); + AddColumn("last_command_check", &Table::ZeroAccessor); + AddColumn("last_log_rotation", &Table::ZeroAccessor); + AddColumn("interval_length", &Table::ZeroAccessor); + AddColumn("num_hosts", &Table::ZeroAccessor); + AddColumn("num_services", &Table::ZeroAccessor); + AddColumn("program_version", &Table::ZeroAccessor); + AddColumn("external_command_buffer_slots", &Table::ZeroAccessor); + AddColumn("external_command_buffer_usage", &Table::ZeroAccessor); + AddColumn("external_command_buffer_max", &Table::ZeroAccessor); + AddColumn("cached_log_messages", &Table::ZeroAccessor); + AddColumn("livestatus_version", &Table::ZeroAccessor); + AddColumn("livestatus_active_connections", &Table::ZeroAccessor); + AddColumn("livestatus_queued_connections", &Table::ZeroAccessor); + AddColumn("livestatus_threads", &Table::ZeroAccessor); +} + +String StatusTable::GetName(void) const +{ + return "status"; +} + +void StatusTable::FetchRows(const function& addRowFn) +{ + Object::Ptr obj = boost::make_shared(); + + /* Return a fake row. */ + addRowFn(obj); +} diff --git a/components/livestatus/statustable.h b/components/livestatus/statustable.h new file mode 100644 index 000000000..5da540064 --- /dev/null +++ b/components/livestatus/statustable.h @@ -0,0 +1,45 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012 Icinga Development Team (http://www.icinga.org/) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#ifndef STATUSTABLE_H +#define STATUSTABLE_H + +namespace livestatus +{ + +/** + * @ingroup livestatus + */ +class StatusTable : public Table +{ +public: + typedef shared_ptr Ptr; + typedef weak_ptr WeakPtr; + + StatusTable(void); + + virtual String GetName(void) const; + +protected: + virtual void FetchRows(const function& addRowFn); +}; + +} + +#endif /* STATUSTABLE_H */ diff --git a/components/livestatus/table.cpp b/components/livestatus/table.cpp new file mode 100644 index 000000000..61e2c1629 --- /dev/null +++ b/components/livestatus/table.cpp @@ -0,0 +1,81 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012 Icinga Development Team (http://www.icinga.org/) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#include "i2-livestatus.h" + +using namespace icinga; +using namespace livestatus; + +Table::Table(void) +{ } + +Table::Ptr Table::GetByName(const String& name) +{ + if (name == "status") + return boost::make_shared(); + + return Table::Ptr(); +} + +void Table::AddColumn(const String& name, const ColumnAccessor& accessor) +{ + m_Columns[name] = accessor; +} + +Table::ColumnAccessor Table::GetColumn(const String& name) const +{ + map::const_iterator it = m_Columns.find(name); + + if (it == m_Columns.end()) + return ColumnAccessor(); + + return it->second; +} + +vector Table::GetColumnNames(void) const +{ + vector names; + + String name; + BOOST_FOREACH(tie(name, tuples::ignore), m_Columns) { + names.push_back(name); + } + + return names; +} + +vector Table::FilterRows(const Filter::Ptr& filter) +{ + vector rs; + + FetchRows(boost::bind(&Table::FilteredAddRow, boost::ref(rs), filter, _1)); + + return rs; +} + +void Table::FilteredAddRow(vector& rs, const Filter::Ptr& filter, const Object::Ptr& object) +{ + if (!filter || filter->Apply(object)) + rs.push_back(object); +} + +Value Table::ZeroAccessor(const Object::Ptr&) +{ + return 0; +} -- 2.40.0