diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 11:32:39 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 11:32:39 +0000 |
commit | 56ae875861ab260b80a030f50c4aff9f9dc8fff0 (patch) | |
tree | 531412110fc901a5918c7f7442202804a83cada9 /lib/livestatus | |
parent | Initial commit. (diff) | |
download | icinga2-upstream/2.14.2.tar.xz icinga2-upstream/2.14.2.zip |
Adding upstream version 2.14.2.upstream/2.14.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
73 files changed, 9344 insertions, 0 deletions
diff --git a/lib/livestatus/CMakeLists.txt b/lib/livestatus/CMakeLists.txt new file mode 100644 index 0000000..d49f9f5 --- /dev/null +++ b/lib/livestatus/CMakeLists.txt @@ -0,0 +1,65 @@ +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ + +mkclass_target(livestatuslistener.ti livestatuslistener-ti.cpp livestatuslistener-ti.hpp) + +set(livestatus_SOURCES + i2-livestatus.hpp + aggregator.cpp aggregator.hpp + andfilter.cpp andfilter.hpp + attributefilter.cpp attributefilter.hpp + avgaggregator.cpp avgaggregator.hpp + column.cpp column.hpp + combinerfilter.cpp combinerfilter.hpp + commandstable.cpp commandstable.hpp + commentstable.cpp commentstable.hpp + contactgroupstable.cpp contactgroupstable.hpp + contactstable.cpp contactstable.hpp + countaggregator.cpp countaggregator.hpp + downtimestable.cpp downtimestable.hpp + endpointstable.cpp endpointstable.hpp + filter.hpp + historytable.hpp + hostgroupstable.cpp hostgroupstable.hpp + hoststable.cpp hoststable.hpp + invavgaggregator.cpp invavgaggregator.hpp + invsumaggregator.cpp invsumaggregator.hpp + livestatuslistener.cpp livestatuslistener.hpp livestatuslistener-ti.hpp + livestatuslogutility.cpp livestatuslogutility.hpp + livestatusquery.cpp livestatusquery.hpp + logtable.cpp logtable.hpp + maxaggregator.cpp maxaggregator.hpp + minaggregator.cpp minaggregator.hpp + negatefilter.cpp negatefilter.hpp + orfilter.cpp orfilter.hpp + servicegroupstable.cpp servicegroupstable.hpp + servicestable.cpp servicestable.hpp + statehisttable.cpp statehisttable.hpp + statustable.cpp statustable.hpp + stdaggregator.cpp stdaggregator.hpp + sumaggregator.cpp sumaggregator.hpp + table.cpp table.hpp + timeperiodstable.cpp timeperiodstable.hpp + zonestable.cpp zonestable.hpp +) + +if(ICINGA2_UNITY_BUILD) + mkunity_target(livestatus livestatus livestatus_SOURCES) +endif() + +add_library(livestatus OBJECT ${livestatus_SOURCES}) + +add_dependencies(livestatus base config icinga remote) + +set_target_properties ( + livestatus PROPERTIES + FOLDER Components +) + +install_if_not_exists( + ${PROJECT_SOURCE_DIR}/etc/icinga2/features-available/livestatus.conf + ${ICINGA2_CONFIGDIR}/features-available +) + +install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${ICINGA2_FULL_INITRUNDIR}/cmd\")") + +set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS}" PARENT_SCOPE) diff --git a/lib/livestatus/aggregator.cpp b/lib/livestatus/aggregator.cpp new file mode 100644 index 0000000..a809b07 --- /dev/null +++ b/lib/livestatus/aggregator.cpp @@ -0,0 +1,18 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/aggregator.hpp" + +using namespace icinga; + +void Aggregator::SetFilter(const Filter::Ptr& filter) +{ + m_Filter = filter; +} + +Filter::Ptr Aggregator::GetFilter() const +{ + return m_Filter; +} + +AggregatorState::~AggregatorState() +{ } diff --git a/lib/livestatus/aggregator.hpp b/lib/livestatus/aggregator.hpp new file mode 100644 index 0000000..1c0f778 --- /dev/null +++ b/lib/livestatus/aggregator.hpp @@ -0,0 +1,44 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef AGGREGATOR_H +#define AGGREGATOR_H + +#include "livestatus/i2-livestatus.hpp" +#include "livestatus/table.hpp" +#include "livestatus/filter.hpp" + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +struct AggregatorState +{ + virtual ~AggregatorState(); +}; + +/** + * @ingroup livestatus + */ +class Aggregator : public Object +{ +public: + DECLARE_PTR_TYPEDEFS(Aggregator); + + virtual void Apply(const Table::Ptr& table, const Value& row, AggregatorState **state) = 0; + virtual double GetResultAndFreeState(AggregatorState *state) const = 0; + void SetFilter(const Filter::Ptr& filter); + +protected: + Aggregator() = default; + + Filter::Ptr GetFilter() const; + +private: + Filter::Ptr m_Filter; +}; + +} + +#endif /* AGGREGATOR_H */ diff --git a/lib/livestatus/andfilter.cpp b/lib/livestatus/andfilter.cpp new file mode 100644 index 0000000..9852580 --- /dev/null +++ b/lib/livestatus/andfilter.cpp @@ -0,0 +1,15 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/andfilter.hpp" + +using namespace icinga; + +bool AndFilter::Apply(const Table::Ptr& table, const Value& row) +{ + for (const Filter::Ptr& filter : m_Filters) { + if (!filter->Apply(table, row)) + return false; + } + + return true; +} diff --git a/lib/livestatus/andfilter.hpp b/lib/livestatus/andfilter.hpp new file mode 100644 index 0000000..8192bf7 --- /dev/null +++ b/lib/livestatus/andfilter.hpp @@ -0,0 +1,26 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef ANDFILTER_H +#define ANDFILTER_H + +#include "livestatus/combinerfilter.hpp" + +using namespace icinga; + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +class AndFilter final : public CombinerFilter +{ +public: + DECLARE_PTR_TYPEDEFS(AndFilter); + + bool Apply(const Table::Ptr& table, const Value& row) override; +}; + +} + +#endif /* ANDFILTER_H */ diff --git a/lib/livestatus/attributefilter.cpp b/lib/livestatus/attributefilter.cpp new file mode 100644 index 0000000..50d7244 --- /dev/null +++ b/lib/livestatus/attributefilter.cpp @@ -0,0 +1,121 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/attributefilter.hpp" +#include "base/convert.hpp" +#include "base/array.hpp" +#include "base/objectlock.hpp" +#include "base/logger.hpp" +#include <boost/regex.hpp> +#include <boost/algorithm/string/predicate.hpp> + +using namespace icinga; + +AttributeFilter::AttributeFilter(String column, String op, String operand) + : m_Column(std::move(column)), m_Operator(std::move(op)), m_Operand(std::move(operand)) +{ } + +bool AttributeFilter::Apply(const Table::Ptr& table, const Value& row) +{ + Column column = table->GetColumn(m_Column); + + Value value = column.ExtractValue(row); + + if (value.IsObjectType<Array>()) { + Array::Ptr array = value; + + if (m_Operator == ">=" || m_Operator == "<") { + bool negate = (m_Operator == "<"); + + ObjectLock olock(array); + for (const String& item : array) { + if (item == m_Operand) + return !negate; /* Item found in list. */ + } + + return negate; /* Item not found in list. */ + } else if (m_Operator == "=") { + return (array->GetLength() == 0); + } else { + BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid operator for column '" + m_Column + "': " + m_Operator + " (expected '>=' or '=').")); + } + } else { + if (m_Operator == "=") { + if (value.GetType() == ValueNumber || value.GetType() == ValueBoolean) + return (static_cast<double>(value) == Convert::ToDouble(m_Operand)); + else + return (static_cast<String>(value) == m_Operand); + } else if (m_Operator == "~") { + bool ret; + try { + boost::regex expr(m_Operand.GetData()); + String operand = value; + boost::smatch what; + ret = boost::regex_search(operand.GetData(), what, expr); + } catch (boost::exception&) { + Log(LogWarning, "AttributeFilter") + << "Regex '" << m_Operand << " " << m_Operator << " " << value << "' error."; + ret = false; + } + + //Log(LogDebug, "LivestatusListener/AttributeFilter") + // << "Attribute filter '" << m_Operand + " " << m_Operator << " " + // << value << "' " << (ret ? "matches" : "doesn't match") << "."; + + return ret; + } else if (m_Operator == "=~") { + bool ret; + try { + String operand = value; + ret = boost::iequals(operand, m_Operand.GetData()); + } catch (boost::exception&) { + Log(LogWarning, "AttributeFilter") + << "Case-insensitive equality '" << m_Operand << " " << m_Operator << " " << value << "' error."; + ret = false; + } + + return ret; + } else if (m_Operator == "~~") { + bool ret; + try { + boost::regex expr(m_Operand.GetData(), boost::regex::icase); + String operand = value; + boost::smatch what; + ret = boost::regex_search(operand.GetData(), what, expr); + } catch (boost::exception&) { + Log(LogWarning, "AttributeFilter") + << "Regex '" << m_Operand << " " << m_Operator << " " << value << "' error."; + ret = false; + } + + //Log(LogDebug, "LivestatusListener/AttributeFilter") + // << "Attribute filter '" << m_Operand << " " << m_Operator << " " + // << value << "' " << (ret ? "matches" : "doesn't match") << "."; + + return ret; + } else if (m_Operator == "<") { + if (value.GetType() == ValueNumber) + return (static_cast<double>(value) < Convert::ToDouble(m_Operand)); + else + return (static_cast<String>(value) < m_Operand); + } else if (m_Operator == ">") { + if (value.GetType() == ValueNumber) + return (static_cast<double>(value) > Convert::ToDouble(m_Operand)); + else + return (static_cast<String>(value) > m_Operand); + } else if (m_Operator == "<=") { + if (value.GetType() == ValueNumber) + return (static_cast<double>(value) <= Convert::ToDouble(m_Operand)); + else + return (static_cast<String>(value) <= m_Operand); + } else if (m_Operator == ">=") { + if (value.GetType() == ValueNumber) + return (static_cast<double>(value) >= Convert::ToDouble(m_Operand)); + else + return (static_cast<String>(value) >= m_Operand); + } else { + BOOST_THROW_EXCEPTION(std::invalid_argument("Unknown operator for column '" + m_Column + "': " + m_Operator)); + } + } + + return false; +} diff --git a/lib/livestatus/attributefilter.hpp b/lib/livestatus/attributefilter.hpp new file mode 100644 index 0000000..18bd843 --- /dev/null +++ b/lib/livestatus/attributefilter.hpp @@ -0,0 +1,33 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef ATTRIBUTEFILTER_H +#define ATTRIBUTEFILTER_H + +#include "livestatus/filter.hpp" + +using namespace icinga; + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +class AttributeFilter final : public Filter +{ +public: + DECLARE_PTR_TYPEDEFS(AttributeFilter); + + AttributeFilter(String column, String op, String operand); + + bool Apply(const Table::Ptr& table, const Value& row) override; + +protected: + String m_Column; + String m_Operator; + String m_Operand; +}; + +} + +#endif /* FILTER_H */ diff --git a/lib/livestatus/avgaggregator.cpp b/lib/livestatus/avgaggregator.cpp new file mode 100644 index 0000000..35701f3 --- /dev/null +++ b/lib/livestatus/avgaggregator.cpp @@ -0,0 +1,38 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/avgaggregator.hpp" + +using namespace icinga; + +AvgAggregator::AvgAggregator(String attr) + : m_AvgAttr(std::move(attr)) +{ } + +AvgAggregatorState *AvgAggregator::EnsureState(AggregatorState **state) +{ + if (!*state) + *state = new AvgAggregatorState(); + + return static_cast<AvgAggregatorState *>(*state); +} + +void AvgAggregator::Apply(const Table::Ptr& table, const Value& row, AggregatorState **state) +{ + Column column = table->GetColumn(m_AvgAttr); + + Value value = column.ExtractValue(row); + + AvgAggregatorState *pstate = EnsureState(state); + + pstate->Avg += value; + pstate->AvgCount++; +} + +double AvgAggregator::GetResultAndFreeState(AggregatorState *state) const +{ + AvgAggregatorState *pstate = EnsureState(&state); + double result = pstate->Avg / pstate->AvgCount; + delete pstate; + + return result; +} diff --git a/lib/livestatus/avgaggregator.hpp b/lib/livestatus/avgaggregator.hpp new file mode 100644 index 0000000..11bd9f3 --- /dev/null +++ b/lib/livestatus/avgaggregator.hpp @@ -0,0 +1,42 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef AVGAGGREGATOR_H +#define AVGAGGREGATOR_H + +#include "livestatus/table.hpp" +#include "livestatus/aggregator.hpp" + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +struct AvgAggregatorState final : public AggregatorState +{ + double Avg{0}; + double AvgCount{0}; +}; + +/** + * @ingroup livestatus + */ +class AvgAggregator final : public Aggregator +{ +public: + DECLARE_PTR_TYPEDEFS(AvgAggregator); + + AvgAggregator(String attr); + + void Apply(const Table::Ptr& table, const Value& row, AggregatorState **state) override; + double GetResultAndFreeState(AggregatorState *state) const override; + +private: + String m_AvgAttr; + + static AvgAggregatorState *EnsureState(AggregatorState **state); +}; + +} + +#endif /* AVGAGGREGATOR_H */ diff --git a/lib/livestatus/column.cpp b/lib/livestatus/column.cpp new file mode 100644 index 0000000..c915b3d --- /dev/null +++ b/lib/livestatus/column.cpp @@ -0,0 +1,21 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/column.hpp" + +using namespace icinga; + +Column::Column(ValueAccessor valueAccessor, ObjectAccessor objectAccessor) + : m_ValueAccessor(std::move(valueAccessor)), m_ObjectAccessor(std::move(objectAccessor)) +{ } + +Value Column::ExtractValue(const Value& urow, LivestatusGroupByType groupByType, const Object::Ptr& groupByObject) const +{ + Value row; + + if (m_ObjectAccessor) + row = m_ObjectAccessor(urow, groupByType, groupByObject); + else + row = urow; + + return m_ValueAccessor(row); +} diff --git a/lib/livestatus/column.hpp b/lib/livestatus/column.hpp new file mode 100644 index 0000000..264cca7 --- /dev/null +++ b/lib/livestatus/column.hpp @@ -0,0 +1,37 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef COLUMN_H +#define COLUMN_H + +#include "livestatus/i2-livestatus.hpp" +#include "base/value.hpp" + +using namespace icinga; + +namespace icinga +{ + +enum LivestatusGroupByType { + LivestatusGroupByNone, + LivestatusGroupByHostGroup, + LivestatusGroupByServiceGroup +}; + +class Column +{ +public: + typedef std::function<Value (const Value&)> ValueAccessor; + typedef std::function<Value (const Value&, LivestatusGroupByType, const Object::Ptr&)> ObjectAccessor; + + Column(ValueAccessor valueAccessor, ObjectAccessor objectAccessor); + + Value ExtractValue(const Value& urow, LivestatusGroupByType groupByType = LivestatusGroupByNone, const Object::Ptr& groupByObject = Empty) const; + +private: + ValueAccessor m_ValueAccessor; + ObjectAccessor m_ObjectAccessor; +}; + +} + +#endif /* COLUMN_H */ diff --git a/lib/livestatus/combinerfilter.cpp b/lib/livestatus/combinerfilter.cpp new file mode 100644 index 0000000..36a8328 --- /dev/null +++ b/lib/livestatus/combinerfilter.cpp @@ -0,0 +1,10 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/combinerfilter.hpp" + +using namespace icinga; + +void CombinerFilter::AddSubFilter(const Filter::Ptr& filter) +{ + m_Filters.push_back(filter); +} diff --git a/lib/livestatus/combinerfilter.hpp b/lib/livestatus/combinerfilter.hpp new file mode 100644 index 0000000..49b8b61 --- /dev/null +++ b/lib/livestatus/combinerfilter.hpp @@ -0,0 +1,31 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef COMBINERFILTER_H +#define COMBINERFILTER_H + +#include "livestatus/filter.hpp" + +using namespace icinga; + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +class CombinerFilter : public Filter +{ +public: + DECLARE_PTR_TYPEDEFS(CombinerFilter); + + void AddSubFilter(const Filter::Ptr& filter); + +protected: + std::vector<Filter::Ptr> m_Filters; + + CombinerFilter() = default; +}; + +} + +#endif /* COMBINERFILTER_H */ diff --git a/lib/livestatus/commandstable.cpp b/lib/livestatus/commandstable.cpp new file mode 100644 index 0000000..3a777d2 --- /dev/null +++ b/lib/livestatus/commandstable.cpp @@ -0,0 +1,142 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/commandstable.hpp" +#include "icinga/icingaapplication.hpp" +#include "icinga/checkcommand.hpp" +#include "icinga/eventcommand.hpp" +#include "icinga/notificationcommand.hpp" +#include "icinga/compatutility.hpp" +#include "base/configtype.hpp" +#include "base/objectlock.hpp" +#include "base/convert.hpp" +#include <boost/algorithm/string/replace.hpp> + +using namespace icinga; + +CommandsTable::CommandsTable() +{ + AddColumns(this); +} + +void CommandsTable::AddColumns(Table *table, const String& prefix, + const Column::ObjectAccessor& objectAccessor) +{ + table->AddColumn(prefix + "name", Column(&CommandsTable::NameAccessor, objectAccessor)); + table->AddColumn(prefix + "line", Column(&CommandsTable::LineAccessor, objectAccessor)); + table->AddColumn(prefix + "custom_variable_names", Column(&CommandsTable::CustomVariableNamesAccessor, objectAccessor)); + table->AddColumn(prefix + "custom_variable_values", Column(&CommandsTable::CustomVariableValuesAccessor, objectAccessor)); + table->AddColumn(prefix + "custom_variables", Column(&CommandsTable::CustomVariablesAccessor, objectAccessor)); + table->AddColumn(prefix + "modified_attributes", Column(&Table::ZeroAccessor, objectAccessor)); + table->AddColumn(prefix + "modified_attributes_list", Column(&Table::ZeroAccessor, objectAccessor)); +} + +String CommandsTable::GetName() const +{ + return "commands"; +} + +String CommandsTable::GetPrefix() const +{ + return "command"; +} + +void CommandsTable::FetchRows(const AddRowFunction& addRowFn) +{ + for (const ConfigObject::Ptr& object : ConfigType::GetObjectsByType<CheckCommand>()) { + if (!addRowFn(object, LivestatusGroupByNone, Empty)) + return; + } + + for (const ConfigObject::Ptr& object : ConfigType::GetObjectsByType<EventCommand>()) { + if (!addRowFn(object, LivestatusGroupByNone, Empty)) + return; + } + + for (const ConfigObject::Ptr& object : ConfigType::GetObjectsByType<NotificationCommand>()) { + if (!addRowFn(object, LivestatusGroupByNone, Empty)) + return; + } +} + +Value CommandsTable::NameAccessor(const Value& row) +{ + Command::Ptr command = static_cast<Command::Ptr>(row); + + return CompatUtility::GetCommandName(command); +} + +Value CommandsTable::LineAccessor(const Value& row) +{ + Command::Ptr command = static_cast<Command::Ptr>(row); + + if (!command) + return Empty; + + return CompatUtility::GetCommandLine(command); +} + +Value CommandsTable::CustomVariableNamesAccessor(const Value& row) +{ + Command::Ptr command = static_cast<Command::Ptr>(row); + + if (!command) + return Empty; + + Dictionary::Ptr vars = command->GetVars(); + + ArrayData keys; + + if (vars) { + ObjectLock xlock(vars); + for (const auto& kv : vars) { + keys.push_back(kv.first); + } + } + + return new Array(std::move(keys)); +} + +Value CommandsTable::CustomVariableValuesAccessor(const Value& row) +{ + Command::Ptr command = static_cast<Command::Ptr>(row); + + if (!command) + return Empty; + + Dictionary::Ptr vars = command->GetVars(); + + ArrayData keys; + + if (vars) { + ObjectLock xlock(vars); + for (const auto& kv : vars) { + keys.push_back(kv.second); + } + } + + return new Array(std::move(keys)); +} + +Value CommandsTable::CustomVariablesAccessor(const Value& row) +{ + Command::Ptr command = static_cast<Command::Ptr>(row); + + if (!command) + return Empty; + + Dictionary::Ptr vars = command->GetVars(); + + ArrayData result; + + if (vars) { + ObjectLock xlock(vars); + for (const auto& kv : vars) { + result.push_back(new Array({ + kv.first, + kv.second + })); + } + } + + return new Array(std::move(result)); +} diff --git a/lib/livestatus/commandstable.hpp b/lib/livestatus/commandstable.hpp new file mode 100644 index 0000000..cd2d915 --- /dev/null +++ b/lib/livestatus/commandstable.hpp @@ -0,0 +1,41 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef COMMANDSTABLE_H +#define COMMANDSTABLE_H + +#include "livestatus/table.hpp" + +using namespace icinga; + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +class CommandsTable final : public Table +{ +public: + DECLARE_PTR_TYPEDEFS(CommandsTable); + + CommandsTable(); + + static void AddColumns(Table *table, const String& prefix = String(), + const Column::ObjectAccessor& objectAccessor = Column::ObjectAccessor()); + + String GetName() const override; + String GetPrefix() const override; + +protected: + void FetchRows(const AddRowFunction& addRowFn) override; + + static Value NameAccessor(const Value& row); + static Value LineAccessor(const Value& row); + static Value CustomVariableNamesAccessor(const Value& row); + static Value CustomVariableValuesAccessor(const Value& row); + static Value CustomVariablesAccessor(const Value& row); +}; + +} + +#endif /* COMMANDSTABLE_H */ diff --git a/lib/livestatus/commentstable.cpp b/lib/livestatus/commentstable.cpp new file mode 100644 index 0000000..40bffad --- /dev/null +++ b/lib/livestatus/commentstable.cpp @@ -0,0 +1,178 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/commentstable.hpp" +#include "livestatus/hoststable.hpp" +#include "livestatus/servicestable.hpp" +#include "icinga/service.hpp" +#include "base/configtype.hpp" +#include "base/objectlock.hpp" + +using namespace icinga; + +CommentsTable::CommentsTable() +{ + AddColumns(this); +} + +void CommentsTable::AddColumns(Table *table, const String& prefix, + const Column::ObjectAccessor& objectAccessor) +{ + table->AddColumn(prefix + "author", Column(&CommentsTable::AuthorAccessor, objectAccessor)); + table->AddColumn(prefix + "comment", Column(&CommentsTable::CommentAccessor, objectAccessor)); + table->AddColumn(prefix + "id", Column(&CommentsTable::IdAccessor, objectAccessor)); + table->AddColumn(prefix + "entry_time", Column(&CommentsTable::EntryTimeAccessor, objectAccessor)); + table->AddColumn(prefix + "type", Column(&CommentsTable::TypeAccessor, objectAccessor)); + table->AddColumn(prefix + "is_service", Column(&CommentsTable::IsServiceAccessor, objectAccessor)); + table->AddColumn(prefix + "persistent", Column(&Table::OneAccessor, objectAccessor)); + table->AddColumn(prefix + "source", Column(&Table::OneAccessor, objectAccessor)); + table->AddColumn(prefix + "entry_type", Column(&CommentsTable::EntryTypeAccessor, objectAccessor)); + table->AddColumn(prefix + "expires", Column(&CommentsTable::ExpiresAccessor, objectAccessor)); + table->AddColumn(prefix + "expire_time", Column(&CommentsTable::ExpireTimeAccessor, objectAccessor)); + + /* order is important - host w/o services must not be empty */ + ServicesTable::AddColumns(table, "service_", [objectAccessor](const Value& row, LivestatusGroupByType, const Object::Ptr&) -> Value { + return ServiceAccessor(row, objectAccessor); + }); + HostsTable::AddColumns(table, "host_", [objectAccessor](const Value& row, LivestatusGroupByType, const Object::Ptr&) -> Value { + return HostAccessor(row, objectAccessor); + }); +} + +String CommentsTable::GetName() const +{ + return "comments"; +} + +String CommentsTable::GetPrefix() const +{ + return "comment"; +} + +void CommentsTable::FetchRows(const AddRowFunction& addRowFn) +{ + for (const Comment::Ptr& comment : ConfigType::GetObjectsByType<Comment>()) { + if (!addRowFn(comment, LivestatusGroupByNone, Empty)) + return; + } +} + +Object::Ptr CommentsTable::HostAccessor(const Value& row, const Column::ObjectAccessor&) +{ + Comment::Ptr comment = static_cast<Comment::Ptr>(row); + + Checkable::Ptr checkable = comment->GetCheckable(); + + Host::Ptr host; + Service::Ptr service; + tie(host, service) = GetHostService(checkable); + + return host; +} + +Object::Ptr CommentsTable::ServiceAccessor(const Value& row, const Column::ObjectAccessor&) +{ + Comment::Ptr comment = static_cast<Comment::Ptr>(row); + + Checkable::Ptr checkable = comment->GetCheckable(); + + Host::Ptr host; + Service::Ptr service; + tie(host, service) = GetHostService(checkable); + + return service; +} + +Value CommentsTable::AuthorAccessor(const Value& row) +{ + Comment::Ptr comment = static_cast<Comment::Ptr>(row); + + if (!comment) + return Empty; + + return comment->GetAuthor(); +} + +Value CommentsTable::CommentAccessor(const Value& row) +{ + Comment::Ptr comment = static_cast<Comment::Ptr>(row); + + if (!comment) + return Empty; + + return comment->GetText(); +} + +Value CommentsTable::IdAccessor(const Value& row) +{ + Comment::Ptr comment = static_cast<Comment::Ptr>(row); + + if (!comment) + return Empty; + + return comment->GetLegacyId(); +} + +Value CommentsTable::EntryTimeAccessor(const Value& row) +{ + Comment::Ptr comment = static_cast<Comment::Ptr>(row); + + if (!comment) + return Empty; + + return static_cast<int>(comment->GetEntryTime()); +} + +Value CommentsTable::TypeAccessor(const Value& row) +{ + Comment::Ptr comment = static_cast<Comment::Ptr>(row); + Checkable::Ptr checkable = comment->GetCheckable(); + + if (!checkable) + return Empty; + + if (dynamic_pointer_cast<Host>(checkable)) + return 1; + else + return 2; +} + +Value CommentsTable::IsServiceAccessor(const Value& row) +{ + Comment::Ptr comment = static_cast<Comment::Ptr>(row); + Checkable::Ptr checkable = comment->GetCheckable(); + + if (!checkable) + return Empty; + + return (dynamic_pointer_cast<Host>(checkable) ? 0 : 1); +} + +Value CommentsTable::EntryTypeAccessor(const Value& row) +{ + Comment::Ptr comment = static_cast<Comment::Ptr>(row); + + if (!comment) + return Empty; + + return comment->GetEntryType(); +} + +Value CommentsTable::ExpiresAccessor(const Value& row) +{ + Comment::Ptr comment = static_cast<Comment::Ptr>(row); + + if (!comment) + return Empty; + + return comment->GetExpireTime() != 0; +} + +Value CommentsTable::ExpireTimeAccessor(const Value& row) +{ + Comment::Ptr comment = static_cast<Comment::Ptr>(row); + + if (!comment) + return Empty; + + return static_cast<int>(comment->GetExpireTime()); +} diff --git a/lib/livestatus/commentstable.hpp b/lib/livestatus/commentstable.hpp new file mode 100644 index 0000000..b46e155 --- /dev/null +++ b/lib/livestatus/commentstable.hpp @@ -0,0 +1,49 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef COMMENTSTABLE_H +#define COMMENTSTABLE_H + +#include "livestatus/table.hpp" + +using namespace icinga; + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +class CommentsTable final : public Table +{ +public: + DECLARE_PTR_TYPEDEFS(CommentsTable); + + CommentsTable(); + + static void AddColumns(Table *table, const String& prefix = String(), + const Column::ObjectAccessor& objectAccessor = Column::ObjectAccessor()); + + String GetName() const override; + String GetPrefix() const override; + +protected: + void FetchRows(const AddRowFunction& addRowFn) override; + +private: + static Object::Ptr HostAccessor(const Value& row, const Column::ObjectAccessor& parentObjectAccessor); + static Object::Ptr ServiceAccessor(const Value& row, const Column::ObjectAccessor& parentObjectAccessor); + + static Value AuthorAccessor(const Value& row); + static Value CommentAccessor(const Value& row); + static Value IdAccessor(const Value& row); + static Value EntryTimeAccessor(const Value& row); + static Value TypeAccessor(const Value& row); + static Value IsServiceAccessor(const Value& row); + static Value EntryTypeAccessor(const Value& row); + static Value ExpiresAccessor(const Value& row); + static Value ExpireTimeAccessor(const Value& row); +}; + +} + +#endif /* COMMENTSTABLE_H */ diff --git a/lib/livestatus/contactgroupstable.cpp b/lib/livestatus/contactgroupstable.cpp new file mode 100644 index 0000000..b4d6853 --- /dev/null +++ b/lib/livestatus/contactgroupstable.cpp @@ -0,0 +1,74 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/contactgroupstable.hpp" +#include "icinga/usergroup.hpp" +#include "base/configtype.hpp" + +using namespace icinga; + +ContactGroupsTable::ContactGroupsTable() +{ + AddColumns(this); +} + +void ContactGroupsTable::AddColumns(Table *table, const String& prefix, + const Column::ObjectAccessor& objectAccessor) +{ + table->AddColumn(prefix + "name", Column(&ContactGroupsTable::NameAccessor, objectAccessor)); + table->AddColumn(prefix + "alias", Column(&ContactGroupsTable::AliasAccessor, objectAccessor)); + table->AddColumn(prefix + "members", Column(&ContactGroupsTable::MembersAccessor, objectAccessor)); +} + +String ContactGroupsTable::GetName() const +{ + return "contactgroups"; +} + +String ContactGroupsTable::GetPrefix() const +{ + return "contactgroup"; +} + +void ContactGroupsTable::FetchRows(const AddRowFunction& addRowFn) +{ + for (const UserGroup::Ptr& ug : ConfigType::GetObjectsByType<UserGroup>()) { + if (!addRowFn(ug, LivestatusGroupByNone, Empty)) + return; + } +} + +Value ContactGroupsTable::NameAccessor(const Value& row) +{ + UserGroup::Ptr user_group = static_cast<UserGroup::Ptr>(row); + + if (!user_group) + return Empty; + + return user_group->GetName(); +} + +Value ContactGroupsTable::AliasAccessor(const Value& row) +{ + UserGroup::Ptr user_group = static_cast<UserGroup::Ptr>(row); + + if (!user_group) + return Empty; + + return user_group->GetName(); +} + +Value ContactGroupsTable::MembersAccessor(const Value& row) +{ + UserGroup::Ptr user_group = static_cast<UserGroup::Ptr>(row); + + if (!user_group) + return Empty; + + ArrayData result; + + for (const User::Ptr& user : user_group->GetMembers()) { + result.push_back(user->GetName()); + } + + return new Array(std::move(result)); +} diff --git a/lib/livestatus/contactgroupstable.hpp b/lib/livestatus/contactgroupstable.hpp new file mode 100644 index 0000000..a57f5c3 --- /dev/null +++ b/lib/livestatus/contactgroupstable.hpp @@ -0,0 +1,39 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef CONTACTGROUPSTABLE_H +#define CONTACTGROUPSTABLE_H + +#include "livestatus/table.hpp" + +using namespace icinga; + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +class ContactGroupsTable final : public Table +{ +public: + DECLARE_PTR_TYPEDEFS(ContactGroupsTable); + + ContactGroupsTable(); + + static void AddColumns(Table *table, const String& prefix = String(), + const Column::ObjectAccessor& objectAccessor = Column::ObjectAccessor()); + + String GetName() const override; + String GetPrefix() const override; + +protected: + void FetchRows(const AddRowFunction& addRowFn) override; + + static Value NameAccessor(const Value& row); + static Value AliasAccessor(const Value& row); + static Value MembersAccessor(const Value& row); +}; + +} + +#endif /* CONTACTGROUPSTABLE_H */ diff --git a/lib/livestatus/contactstable.cpp b/lib/livestatus/contactstable.cpp new file mode 100644 index 0000000..d6a04c4 --- /dev/null +++ b/lib/livestatus/contactstable.cpp @@ -0,0 +1,278 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/contactstable.hpp" +#include "icinga/user.hpp" +#include "icinga/timeperiod.hpp" +#include "icinga/compatutility.hpp" +#include "base/configtype.hpp" +#include "base/objectlock.hpp" +#include "base/json.hpp" +#include "base/utility.hpp" + +using namespace icinga; + +ContactsTable::ContactsTable() +{ + AddColumns(this); +} + +void ContactsTable::AddColumns(Table *table, const String& prefix, + const Column::ObjectAccessor& objectAccessor) +{ + table->AddColumn(prefix + "name", Column(&ContactsTable::NameAccessor, objectAccessor)); + table->AddColumn(prefix + "alias", Column(&ContactsTable::NameAccessor, objectAccessor)); + table->AddColumn(prefix + "email", Column(&ContactsTable::EmailAccessor, objectAccessor)); + table->AddColumn(prefix + "pager", Column(&ContactsTable::PagerAccessor, objectAccessor)); + table->AddColumn(prefix + "host_notification_period", Column(&ContactsTable::HostNotificationPeriodAccessor, objectAccessor)); + table->AddColumn(prefix + "service_notification_period", Column(&ContactsTable::ServiceNotificationPeriodAccessor, objectAccessor)); + table->AddColumn(prefix + "can_submit_commands", Column(&Table::OneAccessor, objectAccessor)); + table->AddColumn(prefix + "host_notifications_enabled", Column(&ContactsTable::HostNotificationsEnabledAccessor, objectAccessor)); + table->AddColumn(prefix + "service_notifications_enabled", Column(&ContactsTable::ServiceNotificationsEnabledAccessor, objectAccessor)); + table->AddColumn(prefix + "in_host_notification_period", Column(&ContactsTable::InHostNotificationPeriodAccessor, objectAccessor)); + table->AddColumn(prefix + "in_service_notification_period", Column(&ContactsTable::InServiceNotificationPeriodAccessor, objectAccessor)); + table->AddColumn(prefix + "vars_variable_names", Column(&ContactsTable::CustomVariableNamesAccessor, objectAccessor)); + table->AddColumn(prefix + "vars_variable_values", Column(&ContactsTable::CustomVariableValuesAccessor, objectAccessor)); + table->AddColumn(prefix + "vars_variables", Column(&ContactsTable::CustomVariablesAccessor, objectAccessor)); + table->AddColumn(prefix + "modified_attributes", Column(&Table::ZeroAccessor, objectAccessor)); + table->AddColumn(prefix + "modified_attributes_list", Column(&Table::ZeroAccessor, objectAccessor)); + table->AddColumn(prefix + "cv_is_json", Column(&ContactsTable::CVIsJsonAccessor, objectAccessor)); + +} + +String ContactsTable::GetName() const +{ + return "contacts"; +} + +String ContactsTable::GetPrefix() const +{ + return "contact"; +} + +void ContactsTable::FetchRows(const AddRowFunction& addRowFn) +{ + for (const User::Ptr& user : ConfigType::GetObjectsByType<User>()) { + if (!addRowFn(user, LivestatusGroupByNone, Empty)) + return; + } +} + +Value ContactsTable::NameAccessor(const Value& row) +{ + User::Ptr user = static_cast<User::Ptr>(row); + + if (!user) + return Empty; + + return user->GetName(); +} + +Value ContactsTable::AliasAccessor(const Value& row) +{ + User::Ptr user = static_cast<User::Ptr>(row); + + if (!user) + return Empty; + + return user->GetDisplayName(); +} + +Value ContactsTable::EmailAccessor(const Value& row) +{ + User::Ptr user = static_cast<User::Ptr>(row); + + if (!user) + return Empty; + + return user->GetEmail(); +} + +Value ContactsTable::PagerAccessor(const Value& row) +{ + User::Ptr user = static_cast<User::Ptr>(row); + + if (!user) + return Empty; + + return user->GetPager(); +} + +Value ContactsTable::HostNotificationPeriodAccessor(const Value& row) +{ + User::Ptr user = static_cast<User::Ptr>(row); + + if (!user) + return Empty; + + /* same as service */ + TimePeriod::Ptr timeperiod = user->GetPeriod(); + + if (!timeperiod) + return Empty; + + return timeperiod->GetName(); +} + +Value ContactsTable::ServiceNotificationPeriodAccessor(const Value& row) +{ + User::Ptr user = static_cast<User::Ptr>(row); + + if (!user) + return Empty; + + TimePeriod::Ptr timeperiod = user->GetPeriod(); + + if (!timeperiod) + return Empty; + + return timeperiod->GetName(); +} + +Value ContactsTable::HostNotificationsEnabledAccessor(const Value& row) +{ + User::Ptr user = static_cast<User::Ptr>(row); + + if (!user) + return Empty; + + return (user->GetEnableNotifications() ? 1 : 0); +} + +Value ContactsTable::ServiceNotificationsEnabledAccessor(const Value& row) +{ + User::Ptr user = static_cast<User::Ptr>(row); + + if (!user) + return Empty; + + return (user->GetEnableNotifications() ? 1 : 0); +} + +Value ContactsTable::InHostNotificationPeriodAccessor(const Value& row) +{ + User::Ptr user = static_cast<User::Ptr>(row); + + if (!user) + return Empty; + + TimePeriod::Ptr timeperiod = user->GetPeriod(); + + if (!timeperiod) + return Empty; + + return (timeperiod->IsInside(Utility::GetTime()) ? 1 : 0); +} + +Value ContactsTable::InServiceNotificationPeriodAccessor(const Value& row) +{ + User::Ptr user = static_cast<User::Ptr>(row); + + if (!user) + return Empty; + + TimePeriod::Ptr timeperiod = user->GetPeriod(); + + if (!timeperiod) + return Empty; + + return (timeperiod->IsInside(Utility::GetTime()) ? 1 : 0); +} + +Value ContactsTable::CustomVariableNamesAccessor(const Value& row) +{ + User::Ptr user = static_cast<User::Ptr>(row); + + if (!user) + return Empty; + + Dictionary::Ptr vars = user->GetVars(); + + ArrayData result; + + if (vars) { + ObjectLock olock(vars); + for (const Dictionary::Pair& kv : vars) { + result.push_back(kv.first); + } + } + + return new Array(std::move(result)); +} + +Value ContactsTable::CustomVariableValuesAccessor(const Value& row) +{ + User::Ptr user = static_cast<User::Ptr>(row); + + if (!user) + return Empty; + + Dictionary::Ptr vars = user->GetVars(); + + ArrayData result; + + if (vars) { + ObjectLock olock(vars); + for (const Dictionary::Pair& kv : vars) { + if (kv.second.IsObjectType<Array>() || kv.second.IsObjectType<Dictionary>()) + result.push_back(JsonEncode(kv.second)); + else + result.push_back(kv.second); + } + } + + return new Array(std::move(result)); +} + +Value ContactsTable::CustomVariablesAccessor(const Value& row) +{ + User::Ptr user = static_cast<User::Ptr>(row); + + if (!user) + return Empty; + + Dictionary::Ptr vars = user->GetVars(); + + ArrayData result; + + if (vars) { + ObjectLock olock(vars); + for (const Dictionary::Pair& kv : vars) { + Value val; + + if (kv.second.IsObjectType<Array>() || kv.second.IsObjectType<Dictionary>()) + val = JsonEncode(kv.second); + else + val = kv.second; + + result.push_back(new Array({ + kv.first, + val + })); + } + } + + return new Array(std::move(result)); +} + +Value ContactsTable::CVIsJsonAccessor(const Value& row) +{ + User::Ptr user = static_cast<User::Ptr>(row); + + if (!user) + return Empty; + + Dictionary::Ptr vars = user->GetVars(); + + if (!vars) + return Empty; + + bool cv_is_json = false; + + ObjectLock olock(vars); + for (const Dictionary::Pair& kv : vars) { + if (kv.second.IsObjectType<Array>() || kv.second.IsObjectType<Dictionary>()) + cv_is_json = true; + } + + return cv_is_json; +} diff --git a/lib/livestatus/contactstable.hpp b/lib/livestatus/contactstable.hpp new file mode 100644 index 0000000..0bd2679 --- /dev/null +++ b/lib/livestatus/contactstable.hpp @@ -0,0 +1,50 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef CONTACTSTABLE_H +#define CONTACTSTABLE_H + +#include "livestatus/table.hpp" + +using namespace icinga; + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +class ContactsTable final : public Table +{ +public: + DECLARE_PTR_TYPEDEFS(ContactsTable); + + ContactsTable(); + + static void AddColumns(Table *table, const String& prefix = String(), + const Column::ObjectAccessor& objectAccessor = Column::ObjectAccessor()); + + String GetName() const override; + String GetPrefix() const override; + +protected: + void FetchRows(const AddRowFunction& addRowFn) override; + + static Value NameAccessor(const Value& row); + static Value AliasAccessor(const Value& row); + static Value EmailAccessor(const Value& row); + static Value PagerAccessor(const Value& row); + static Value HostNotificationPeriodAccessor(const Value& row); + static Value ServiceNotificationPeriodAccessor(const Value& row); + static Value HostNotificationsEnabledAccessor(const Value& row); + static Value ServiceNotificationsEnabledAccessor(const Value& row); + static Value InHostNotificationPeriodAccessor(const Value& row); + static Value InServiceNotificationPeriodAccessor(const Value& row); + static Value CustomVariableNamesAccessor(const Value& row); + static Value CustomVariableValuesAccessor(const Value& row); + static Value CustomVariablesAccessor(const Value& row); + static Value CVIsJsonAccessor(const Value& row); +}; + +} + +#endif /* CONTACTSTABLE_H */ diff --git a/lib/livestatus/countaggregator.cpp b/lib/livestatus/countaggregator.cpp new file mode 100644 index 0000000..b8a7238 --- /dev/null +++ b/lib/livestatus/countaggregator.cpp @@ -0,0 +1,30 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/countaggregator.hpp" + +using namespace icinga; + +CountAggregatorState *CountAggregator::EnsureState(AggregatorState **state) +{ + if (!*state) + *state = new CountAggregatorState(); + + return static_cast<CountAggregatorState *>(*state); +} + +void CountAggregator::Apply(const Table::Ptr& table, const Value& row, AggregatorState **state) +{ + CountAggregatorState *pstate = EnsureState(state); + + if (GetFilter()->Apply(table, row)) + pstate->Count++; +} + +double CountAggregator::GetResultAndFreeState(AggregatorState *state) const +{ + CountAggregatorState *pstate = EnsureState(&state); + double result = pstate->Count; + delete pstate; + + return result; +} diff --git a/lib/livestatus/countaggregator.hpp b/lib/livestatus/countaggregator.hpp new file mode 100644 index 0000000..22d4983 --- /dev/null +++ b/lib/livestatus/countaggregator.hpp @@ -0,0 +1,37 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef COUNTAGGREGATOR_H +#define COUNTAGGREGATOR_H + +#include "livestatus/table.hpp" +#include "livestatus/aggregator.hpp" + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +struct CountAggregatorState final : public AggregatorState +{ + int Count{0}; +}; + +/** + * @ingroup livestatus + */ +class CountAggregator final : public Aggregator +{ +public: + DECLARE_PTR_TYPEDEFS(CountAggregator); + + void Apply(const Table::Ptr& table, const Value& row, AggregatorState **) override; + double GetResultAndFreeState(AggregatorState *state) const override; + +private: + static CountAggregatorState *EnsureState(AggregatorState **state); +}; + +} + +#endif /* COUNTAGGREGATOR_H */ diff --git a/lib/livestatus/downtimestable.cpp b/lib/livestatus/downtimestable.cpp new file mode 100644 index 0000000..09c111e --- /dev/null +++ b/lib/livestatus/downtimestable.cpp @@ -0,0 +1,168 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/downtimestable.hpp" +#include "livestatus/hoststable.hpp" +#include "livestatus/servicestable.hpp" +#include "icinga/service.hpp" +#include "base/configtype.hpp" +#include "base/objectlock.hpp" + +using namespace icinga; + +DowntimesTable::DowntimesTable() +{ + AddColumns(this); +} + +void DowntimesTable::AddColumns(Table *table, const String& prefix, + const Column::ObjectAccessor& objectAccessor) +{ + table->AddColumn(prefix + "author", Column(&DowntimesTable::AuthorAccessor, objectAccessor)); + table->AddColumn(prefix + "comment", Column(&DowntimesTable::CommentAccessor, objectAccessor)); + table->AddColumn(prefix + "id", Column(&DowntimesTable::IdAccessor, objectAccessor)); + table->AddColumn(prefix + "entry_time", Column(&DowntimesTable::EntryTimeAccessor, objectAccessor)); + table->AddColumn(prefix + "type", Column(&DowntimesTable::TypeAccessor, objectAccessor)); + table->AddColumn(prefix + "is_service", Column(&DowntimesTable::IsServiceAccessor, objectAccessor)); + table->AddColumn(prefix + "start_time", Column(&DowntimesTable::StartTimeAccessor, objectAccessor)); + table->AddColumn(prefix + "end_time", Column(&DowntimesTable::EndTimeAccessor, objectAccessor)); + table->AddColumn(prefix + "fixed", Column(&DowntimesTable::FixedAccessor, objectAccessor)); + table->AddColumn(prefix + "duration", Column(&DowntimesTable::DurationAccessor, objectAccessor)); + table->AddColumn(prefix + "triggered_by", Column(&DowntimesTable::TriggeredByAccessor, objectAccessor)); + + /* order is important - host w/o services must not be empty */ + ServicesTable::AddColumns(table, "service_", [objectAccessor](const Value& row, LivestatusGroupByType, const Object::Ptr&) -> Value { + return ServiceAccessor(row, objectAccessor); + }); + HostsTable::AddColumns(table, "host_", [objectAccessor](const Value& row, LivestatusGroupByType, const Object::Ptr&) -> Value { + return HostAccessor(row, objectAccessor); + }); +} + +String DowntimesTable::GetName() const +{ + return "downtimes"; +} + +String DowntimesTable::GetPrefix() const +{ + return "downtime"; +} + +void DowntimesTable::FetchRows(const AddRowFunction& addRowFn) +{ + for (const Downtime::Ptr& downtime : ConfigType::GetObjectsByType<Downtime>()) { + if (!addRowFn(downtime, LivestatusGroupByNone, Empty)) + return; + } +} + +Object::Ptr DowntimesTable::HostAccessor(const Value& row, const Column::ObjectAccessor&) +{ + Downtime::Ptr downtime = static_cast<Downtime::Ptr>(row); + + Checkable::Ptr checkable = downtime->GetCheckable(); + + Host::Ptr host; + Service::Ptr service; + tie(host, service) = GetHostService(checkable); + + return host; +} + +Object::Ptr DowntimesTable::ServiceAccessor(const Value& row, const Column::ObjectAccessor&) +{ + Downtime::Ptr downtime = static_cast<Downtime::Ptr>(row); + + Checkable::Ptr checkable = downtime->GetCheckable(); + + Host::Ptr host; + Service::Ptr service; + tie(host, service) = GetHostService(checkable); + + return service; +} + +Value DowntimesTable::AuthorAccessor(const Value& row) +{ + Downtime::Ptr downtime = static_cast<Downtime::Ptr>(row); + + return downtime->GetAuthor(); +} + +Value DowntimesTable::CommentAccessor(const Value& row) +{ + Downtime::Ptr downtime = static_cast<Downtime::Ptr>(row); + + return downtime->GetComment(); +} + +Value DowntimesTable::IdAccessor(const Value& row) +{ + Downtime::Ptr downtime = static_cast<Downtime::Ptr>(row); + + return downtime->GetLegacyId(); +} + +Value DowntimesTable::EntryTimeAccessor(const Value& row) +{ + Downtime::Ptr downtime = static_cast<Downtime::Ptr>(row); + + return static_cast<int>(downtime->GetEntryTime()); +} + +Value DowntimesTable::TypeAccessor(const Value& row) +{ + Downtime::Ptr downtime = static_cast<Downtime::Ptr>(row); + // 1 .. active, 0 .. pending + return (downtime->IsInEffect() ? 1 : 0); +} + +Value DowntimesTable::IsServiceAccessor(const Value& row) +{ + Downtime::Ptr downtime = static_cast<Downtime::Ptr>(row); + Checkable::Ptr checkable = downtime->GetCheckable(); + + return (dynamic_pointer_cast<Host>(checkable) ? 0 : 1); +} + +Value DowntimesTable::StartTimeAccessor(const Value& row) +{ + Downtime::Ptr downtime = static_cast<Downtime::Ptr>(row); + + return static_cast<int>(downtime->GetStartTime()); +} + +Value DowntimesTable::EndTimeAccessor(const Value& row) +{ + Downtime::Ptr downtime = static_cast<Downtime::Ptr>(row); + + return static_cast<int>(downtime->GetEndTime()); +} + +Value DowntimesTable::FixedAccessor(const Value& row) +{ + Downtime::Ptr downtime = static_cast<Downtime::Ptr>(row); + + return downtime->GetFixed(); +} + +Value DowntimesTable::DurationAccessor(const Value& row) +{ + Downtime::Ptr downtime = static_cast<Downtime::Ptr>(row); + + return downtime->GetDuration(); +} + +Value DowntimesTable::TriggeredByAccessor(const Value& row) +{ + Downtime::Ptr downtime = static_cast<Downtime::Ptr>(row); + + String triggerDowntimeName = downtime->GetTriggeredBy(); + + Downtime::Ptr triggerDowntime = Downtime::GetByName(triggerDowntimeName); + + if (triggerDowntime) + return triggerDowntime->GetLegacyId(); + + return Empty; +} diff --git a/lib/livestatus/downtimestable.hpp b/lib/livestatus/downtimestable.hpp new file mode 100644 index 0000000..4b5c909 --- /dev/null +++ b/lib/livestatus/downtimestable.hpp @@ -0,0 +1,51 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef DOWNTIMESTABLE_H +#define DOWNTIMESTABLE_H + +#include "livestatus/table.hpp" + +using namespace icinga; + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +class DowntimesTable final : public Table +{ +public: + DECLARE_PTR_TYPEDEFS(DowntimesTable); + + DowntimesTable(); + + static void AddColumns(Table *table, const String& prefix = String(), + const Column::ObjectAccessor& objectAccessor = Column::ObjectAccessor()); + + String GetName() const override; + String GetPrefix() const override; + +protected: + void FetchRows(const AddRowFunction& addRowFn) override; + +private: + static Object::Ptr HostAccessor(const Value& row, const Column::ObjectAccessor& parentObjectAccessor); + static Object::Ptr ServiceAccessor(const Value& row, const Column::ObjectAccessor& parentObjectAccessor); + + static Value AuthorAccessor(const Value& row); + static Value CommentAccessor(const Value& row); + static Value IdAccessor(const Value& row); + static Value EntryTimeAccessor(const Value& row); + static Value TypeAccessor(const Value& row); + static Value IsServiceAccessor(const Value& row); + static Value StartTimeAccessor(const Value& row); + static Value EndTimeAccessor(const Value& row); + static Value FixedAccessor(const Value& row); + static Value DurationAccessor(const Value& row); + static Value TriggeredByAccessor(const Value& row); +}; + +} + +#endif /* DOWNTIMESTABLE_H */ diff --git a/lib/livestatus/endpointstable.cpp b/lib/livestatus/endpointstable.cpp new file mode 100644 index 0000000..3d407eb --- /dev/null +++ b/lib/livestatus/endpointstable.cpp @@ -0,0 +1,109 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/endpointstable.hpp" +#include "icinga/host.hpp" +#include "icinga/service.hpp" +#include "icinga/icingaapplication.hpp" +#include "remote/endpoint.hpp" +#include "remote/zone.hpp" +#include "base/configtype.hpp" +#include "base/objectlock.hpp" +#include "base/convert.hpp" +#include "base/utility.hpp" +#include <boost/algorithm/string/replace.hpp> + +using namespace icinga; + +EndpointsTable::EndpointsTable() +{ + AddColumns(this); +} + +void EndpointsTable::AddColumns(Table *table, const String& prefix, + const Column::ObjectAccessor& objectAccessor) +{ + table->AddColumn(prefix + "name", Column(&EndpointsTable::NameAccessor, objectAccessor)); + table->AddColumn(prefix + "identity", Column(&EndpointsTable::IdentityAccessor, objectAccessor)); + table->AddColumn(prefix + "node", Column(&EndpointsTable::NodeAccessor, objectAccessor)); + table->AddColumn(prefix + "is_connected", Column(&EndpointsTable::IsConnectedAccessor, objectAccessor)); + table->AddColumn(prefix + "zone", Column(&EndpointsTable::ZoneAccessor, objectAccessor)); +} + +String EndpointsTable::GetName() const +{ + return "endpoints"; +} + +String EndpointsTable::GetPrefix() const +{ + return "endpoint"; +} + +void EndpointsTable::FetchRows(const AddRowFunction& addRowFn) +{ + for (const Endpoint::Ptr& endpoint : ConfigType::GetObjectsByType<Endpoint>()) { + if (!addRowFn(endpoint, LivestatusGroupByNone, Empty)) + return; + } +} + +Value EndpointsTable::NameAccessor(const Value& row) +{ + Endpoint::Ptr endpoint = static_cast<Endpoint::Ptr>(row); + + if (!endpoint) + return Empty; + + return endpoint->GetName(); +} + +Value EndpointsTable::IdentityAccessor(const Value& row) +{ + Endpoint::Ptr endpoint = static_cast<Endpoint::Ptr>(row); + + if (!endpoint) + return Empty; + + return endpoint->GetName(); +} + +Value EndpointsTable::NodeAccessor(const Value& row) +{ + Endpoint::Ptr endpoint = static_cast<Endpoint::Ptr>(row); + + if (!endpoint) + return Empty; + + return IcingaApplication::GetInstance()->GetNodeName(); +} + +Value EndpointsTable::IsConnectedAccessor(const Value& row) +{ + Endpoint::Ptr endpoint = static_cast<Endpoint::Ptr>(row); + + if (!endpoint) + return Empty; + + unsigned int is_connected = endpoint->GetConnected() ? 1 : 0; + + /* if identity is equal to node, fake is_connected */ + if (endpoint->GetName() == IcingaApplication::GetInstance()->GetNodeName()) + is_connected = 1; + + return is_connected; +} + +Value EndpointsTable::ZoneAccessor(const Value& row) +{ + Endpoint::Ptr endpoint = static_cast<Endpoint::Ptr>(row); + + if (!endpoint) + return Empty; + + Zone::Ptr zone = endpoint->GetZone(); + + if (!zone) + return Empty; + + return zone->GetName(); +} diff --git a/lib/livestatus/endpointstable.hpp b/lib/livestatus/endpointstable.hpp new file mode 100644 index 0000000..7d011ef --- /dev/null +++ b/lib/livestatus/endpointstable.hpp @@ -0,0 +1,41 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef ENDPOINTSTABLE_H +#define ENDPOINTSTABLE_H + +#include "livestatus/table.hpp" + +using namespace icinga; + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +class EndpointsTable final : public Table +{ +public: + DECLARE_PTR_TYPEDEFS(EndpointsTable); + + EndpointsTable(); + + static void AddColumns(Table *table, const String& prefix = String(), + const Column::ObjectAccessor& objectAccessor = Column::ObjectAccessor()); + + String GetName() const override; + String GetPrefix() const override; + +protected: + void FetchRows(const AddRowFunction& addRowFn) override; + + static Value NameAccessor(const Value& row); + static Value IdentityAccessor(const Value& row); + static Value NodeAccessor(const Value& row); + static Value IsConnectedAccessor(const Value& row); + static Value ZoneAccessor(const Value& row); +}; + +} + +#endif /* ENDPOINTSTABLE_H */ diff --git a/lib/livestatus/filter.hpp b/lib/livestatus/filter.hpp new file mode 100644 index 0000000..b9a01c8 --- /dev/null +++ b/lib/livestatus/filter.hpp @@ -0,0 +1,28 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef FILTER_H +#define FILTER_H + +#include "livestatus/i2-livestatus.hpp" +#include "livestatus/table.hpp" + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +class Filter : public Object +{ +public: + DECLARE_PTR_TYPEDEFS(Filter); + + virtual bool Apply(const Table::Ptr& table, const Value& row) = 0; + +protected: + Filter() = default; +}; + +} + +#endif /* FILTER_H */ diff --git a/lib/livestatus/historytable.hpp b/lib/livestatus/historytable.hpp new file mode 100644 index 0000000..f117857 --- /dev/null +++ b/lib/livestatus/historytable.hpp @@ -0,0 +1,24 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef HISTORYTABLE_H +#define HISTORYTABLE_H + +#include "livestatus/table.hpp" +#include "base/dictionary.hpp" + +namespace icinga +{ + + +/** + * @ingroup livestatus + */ +class HistoryTable : public Table +{ +public: + virtual void UpdateLogEntries(const Dictionary::Ptr& bag, int line_count, int lineno, const AddRowFunction& addRowFn) = 0; +}; + +} + +#endif /* HISTORYTABLE_H */ diff --git a/lib/livestatus/hostgroupstable.cpp b/lib/livestatus/hostgroupstable.cpp new file mode 100644 index 0000000..984eddb --- /dev/null +++ b/lib/livestatus/hostgroupstable.cpp @@ -0,0 +1,473 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/hostgroupstable.hpp" +#include "icinga/hostgroup.hpp" +#include "icinga/host.hpp" +#include "icinga/service.hpp" +#include "base/configtype.hpp" + +using namespace icinga; + +HostGroupsTable::HostGroupsTable() +{ + AddColumns(this); +} + +void HostGroupsTable::AddColumns(Table *table, const String& prefix, + const Column::ObjectAccessor& objectAccessor) +{ + table->AddColumn(prefix + "name", Column(&HostGroupsTable::NameAccessor, objectAccessor)); + table->AddColumn(prefix + "alias", Column(&HostGroupsTable::AliasAccessor, objectAccessor)); + table->AddColumn(prefix + "notes", Column(&HostGroupsTable::NotesAccessor, objectAccessor)); + table->AddColumn(prefix + "notes_url", Column(&HostGroupsTable::NotesUrlAccessor, objectAccessor)); + table->AddColumn(prefix + "action_url", Column(&HostGroupsTable::ActionUrlAccessor, objectAccessor)); + table->AddColumn(prefix + "members", Column(&HostGroupsTable::MembersAccessor, objectAccessor)); + table->AddColumn(prefix + "members_with_state", Column(&HostGroupsTable::MembersWithStateAccessor, objectAccessor)); + table->AddColumn(prefix + "worst_host_state", Column(&HostGroupsTable::WorstHostStateAccessor, objectAccessor)); + table->AddColumn(prefix + "num_hosts", Column(&HostGroupsTable::NumHostsAccessor, objectAccessor)); + table->AddColumn(prefix + "num_hosts_pending", Column(&HostGroupsTable::NumHostsPendingAccessor, objectAccessor)); + table->AddColumn(prefix + "num_hosts_up", Column(&HostGroupsTable::NumHostsUpAccessor, objectAccessor)); + table->AddColumn(prefix + "num_hosts_down", Column(&HostGroupsTable::NumHostsDownAccessor, objectAccessor)); + table->AddColumn(prefix + "num_hosts_unreach", Column(&HostGroupsTable::NumHostsUnreachAccessor, objectAccessor)); + table->AddColumn(prefix + "num_services", Column(&HostGroupsTable::NumServicesAccessor, objectAccessor)); + table->AddColumn(prefix + "worst_service_state", Column(&HostGroupsTable::WorstServiceStateAccessor, objectAccessor)); + table->AddColumn(prefix + "num_services_pending", Column(&HostGroupsTable::NumServicesPendingAccessor, objectAccessor)); + table->AddColumn(prefix + "num_services_ok", Column(&HostGroupsTable::NumServicesOkAccessor, objectAccessor)); + table->AddColumn(prefix + "num_services_warn", Column(&HostGroupsTable::NumServicesWarnAccessor, objectAccessor)); + table->AddColumn(prefix + "num_services_crit", Column(&HostGroupsTable::NumServicesCritAccessor, objectAccessor)); + table->AddColumn(prefix + "num_services_unknown", Column(&HostGroupsTable::NumServicesUnknownAccessor, objectAccessor)); + table->AddColumn(prefix + "worst_service_hard_state", Column(&HostGroupsTable::WorstServiceHardStateAccessor, objectAccessor)); + table->AddColumn(prefix + "num_services_hard_ok", Column(&HostGroupsTable::NumServicesHardOkAccessor, objectAccessor)); + table->AddColumn(prefix + "num_services_hard_warn", Column(&HostGroupsTable::NumServicesHardWarnAccessor, objectAccessor)); + table->AddColumn(prefix + "num_services_hard_crit", Column(&HostGroupsTable::NumServicesHardCritAccessor, objectAccessor)); + table->AddColumn(prefix + "num_services_hard_unknown", Column(&HostGroupsTable::NumServicesHardUnknownAccessor, objectAccessor)); +} + +String HostGroupsTable::GetName() const +{ + return "hostgroups"; +} + +String HostGroupsTable::GetPrefix() const +{ + return "hostgroup"; +} + +void HostGroupsTable::FetchRows(const AddRowFunction& addRowFn) +{ + for (const HostGroup::Ptr& hg : ConfigType::GetObjectsByType<HostGroup>()) { + if (!addRowFn(hg, LivestatusGroupByNone, Empty)) + return; + } +} + +Value HostGroupsTable::NameAccessor(const Value& row) +{ + HostGroup::Ptr hg = static_cast<HostGroup::Ptr>(row); + + if (!hg) + return Empty; + + return hg->GetName(); +} + +Value HostGroupsTable::AliasAccessor(const Value& row) +{ + HostGroup::Ptr hg = static_cast<HostGroup::Ptr>(row); + + if (!hg) + return Empty; + + return hg->GetDisplayName(); +} + +Value HostGroupsTable::NotesAccessor(const Value& row) +{ + HostGroup::Ptr hg = static_cast<HostGroup::Ptr>(row); + + if (!hg) + return Empty; + + return hg->GetNotes(); +} + +Value HostGroupsTable::NotesUrlAccessor(const Value& row) +{ + HostGroup::Ptr hg = static_cast<HostGroup::Ptr>(row); + + if (!hg) + return Empty; + + return hg->GetNotesUrl(); +} + +Value HostGroupsTable::ActionUrlAccessor(const Value& row) +{ + HostGroup::Ptr hg = static_cast<HostGroup::Ptr>(row); + + if (!hg) + return Empty; + + return hg->GetActionUrl(); +} + +Value HostGroupsTable::MembersAccessor(const Value& row) +{ + HostGroup::Ptr hg = static_cast<HostGroup::Ptr>(row); + + if (!hg) + return Empty; + + ArrayData members; + + for (const Host::Ptr& host : hg->GetMembers()) { + members.push_back(host->GetName()); + } + + return new Array(std::move(members)); +} + +Value HostGroupsTable::MembersWithStateAccessor(const Value& row) +{ + HostGroup::Ptr hg = static_cast<HostGroup::Ptr>(row); + + if (!hg) + return Empty; + + ArrayData members; + + for (const Host::Ptr& host : hg->GetMembers()) { + members.push_back(new Array({ + host->GetName(), + host->GetState() + })); + } + + return new Array(std::move(members)); +} + +Value HostGroupsTable::WorstHostStateAccessor(const Value& row) +{ + HostGroup::Ptr hg = static_cast<HostGroup::Ptr>(row); + + if (!hg) + return Empty; + + int worst_host = HostUp; + + for (const Host::Ptr& host : hg->GetMembers()) { + if (host->GetState() > worst_host) + worst_host = host->GetState(); + } + + return worst_host; +} + +Value HostGroupsTable::NumHostsAccessor(const Value& row) +{ + HostGroup::Ptr hg = static_cast<HostGroup::Ptr>(row); + + if (!hg) + return Empty; + + return hg->GetMembers().size(); +} + +Value HostGroupsTable::NumHostsPendingAccessor(const Value& row) +{ + HostGroup::Ptr hg = static_cast<HostGroup::Ptr>(row); + + if (!hg) + return Empty; + + int num_hosts = 0; + + for (const Host::Ptr& host : hg->GetMembers()) { + /* no checkresult */ + if (!host->GetLastCheckResult()) + num_hosts++; + } + + return num_hosts; +} + +Value HostGroupsTable::NumHostsUpAccessor(const Value& row) +{ + HostGroup::Ptr hg = static_cast<HostGroup::Ptr>(row); + + if (!hg) + return Empty; + + int num_hosts = 0; + + for (const Host::Ptr& host : hg->GetMembers()) { + if (host->GetState() == HostUp) + num_hosts++; + } + + return num_hosts; +} + +Value HostGroupsTable::NumHostsDownAccessor(const Value& row) +{ + HostGroup::Ptr hg = static_cast<HostGroup::Ptr>(row); + + if (!hg) + return Empty; + + int num_hosts = 0; + + for (const Host::Ptr& host : hg->GetMembers()) { + if (host->GetState() == HostDown) + num_hosts++; + } + + return num_hosts; +} + +Value HostGroupsTable::NumHostsUnreachAccessor(const Value& row) +{ + HostGroup::Ptr hg = static_cast<HostGroup::Ptr>(row); + + if (!hg) + return Empty; + + int num_hosts = 0; + + for (const Host::Ptr& host : hg->GetMembers()) { + if (!host->IsReachable()) + num_hosts++; + } + + return num_hosts; +} + +Value HostGroupsTable::NumServicesAccessor(const Value& row) +{ + HostGroup::Ptr hg = static_cast<HostGroup::Ptr>(row); + + if (!hg) + return Empty; + + int num_services = 0; + + if (hg->GetMembers().size() == 0) + return 0; + + for (const Host::Ptr& host : hg->GetMembers()) { + num_services += host->GetServices().size(); + } + + return num_services; +} + +Value HostGroupsTable::WorstServiceStateAccessor(const Value& row) +{ + HostGroup::Ptr hg = static_cast<HostGroup::Ptr>(row); + + if (!hg) + return Empty; + + Value worst_service = ServiceOK; + + for (const Host::Ptr& host : hg->GetMembers()) { + for (const Service::Ptr& service : host->GetServices()) { + if (service->GetState() > worst_service) + worst_service = service->GetState(); + } + } + + return worst_service; +} + +Value HostGroupsTable::NumServicesPendingAccessor(const Value& row) +{ + HostGroup::Ptr hg = static_cast<HostGroup::Ptr>(row); + + if (!hg) + return Empty; + + int num_services = 0; + + for (const Host::Ptr& host : hg->GetMembers()) { + for (const Service::Ptr& service : host->GetServices()) { + if (!service->GetLastCheckResult()) + num_services++; + } + } + + return num_services; +} + +Value HostGroupsTable::NumServicesOkAccessor(const Value& row) +{ + HostGroup::Ptr hg = static_cast<HostGroup::Ptr>(row); + + if (!hg) + return Empty; + + int num_services = 0; + + for (const Host::Ptr& host : hg->GetMembers()) { + for (const Service::Ptr& service : host->GetServices()) { + if (service->GetState() == ServiceOK) + num_services++; + } + } + + return num_services; +} + +Value HostGroupsTable::NumServicesWarnAccessor(const Value& row) +{ + HostGroup::Ptr hg = static_cast<HostGroup::Ptr>(row); + + if (!hg) + return Empty; + + int num_services = 0; + + for (const Host::Ptr& host : hg->GetMembers()) { + for (const Service::Ptr& service : host->GetServices()) { + if (service->GetState() == ServiceWarning) + num_services++; + } + } + + return num_services; +} + +Value HostGroupsTable::NumServicesCritAccessor(const Value& row) +{ + HostGroup::Ptr hg = static_cast<HostGroup::Ptr>(row); + + if (!hg) + return Empty; + + int num_services = 0; + + for (const Host::Ptr& host : hg->GetMembers()) { + for (const Service::Ptr& service : host->GetServices()) { + if (service->GetState() == ServiceCritical) + num_services++; + } + } + + return num_services; +} + +Value HostGroupsTable::NumServicesUnknownAccessor(const Value& row) +{ + HostGroup::Ptr hg = static_cast<HostGroup::Ptr>(row); + + if (!hg) + return Empty; + + int num_services = 0; + + for (const Host::Ptr& host : hg->GetMembers()) { + for (const Service::Ptr& service : host->GetServices()) { + if (service->GetState() == ServiceUnknown) + num_services++; + } + } + + return num_services; +} + +Value HostGroupsTable::WorstServiceHardStateAccessor(const Value& row) +{ + HostGroup::Ptr hg = static_cast<HostGroup::Ptr>(row); + + if (!hg) + return Empty; + + Value worst_service = ServiceOK; + + for (const Host::Ptr& host : hg->GetMembers()) { + for (const Service::Ptr& service : host->GetServices()) { + if (service->GetStateType() == StateTypeHard) { + if (service->GetState() > worst_service) + worst_service = service->GetState(); + } + } + } + + return worst_service; +} + +Value HostGroupsTable::NumServicesHardOkAccessor(const Value& row) +{ + HostGroup::Ptr hg = static_cast<HostGroup::Ptr>(row); + + if (!hg) + return Empty; + + int num_services = 0; + + for (const Host::Ptr& host : hg->GetMembers()) { + for (const Service::Ptr& service : host->GetServices()) { + if (service->GetStateType() == StateTypeHard && service->GetState() == ServiceOK) + num_services++; + } + } + + return num_services; +} + +Value HostGroupsTable::NumServicesHardWarnAccessor(const Value& row) +{ + HostGroup::Ptr hg = static_cast<HostGroup::Ptr>(row); + + if (!hg) + return Empty; + + int num_services = 0; + + for (const Host::Ptr& host : hg->GetMembers()) { + for (const Service::Ptr& service : host->GetServices()) { + if (service->GetStateType() == StateTypeHard && service->GetState() == ServiceWarning) + num_services++; + } + } + + return num_services; +} + +Value HostGroupsTable::NumServicesHardCritAccessor(const Value& row) +{ + HostGroup::Ptr hg = static_cast<HostGroup::Ptr>(row); + + if (!hg) + return Empty; + + int num_services = 0; + + for (const Host::Ptr& host : hg->GetMembers()) { + for (const Service::Ptr& service : host->GetServices()) { + if (service->GetStateType() == StateTypeHard && service->GetState() == ServiceCritical) + num_services++; + } + } + + return num_services; +} + +Value HostGroupsTable::NumServicesHardUnknownAccessor(const Value& row) +{ + HostGroup::Ptr hg = static_cast<HostGroup::Ptr>(row); + + if (!hg) + return Empty; + + int num_services = 0; + + for (const Host::Ptr& host : hg->GetMembers()) { + for (const Service::Ptr& service : host->GetServices()) { + if (service->GetStateType() == StateTypeHard && service->GetState() == ServiceUnknown) + num_services++; + } + } + + return num_services; +} diff --git a/lib/livestatus/hostgroupstable.hpp b/lib/livestatus/hostgroupstable.hpp new file mode 100644 index 0000000..cc5039f --- /dev/null +++ b/lib/livestatus/hostgroupstable.hpp @@ -0,0 +1,61 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef HOSTGROUPSTABLE_H +#define HOSTGROUPSTABLE_H + +#include "livestatus/table.hpp" + +using namespace icinga; + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +class HostGroupsTable final : public Table +{ +public: + DECLARE_PTR_TYPEDEFS(HostGroupsTable); + + HostGroupsTable(); + + static void AddColumns(Table *table, const String& prefix = String(), + const Column::ObjectAccessor& objectAccessor = Column::ObjectAccessor()); + + String GetName() const override; + String GetPrefix() const override; + +protected: + void FetchRows(const AddRowFunction& addRowFn) override; + + static Value NameAccessor(const Value& row); + static Value AliasAccessor(const Value& row); + static Value NotesAccessor(const Value& row); + static Value NotesUrlAccessor(const Value& row); + static Value ActionUrlAccessor(const Value& row); + static Value MembersAccessor(const Value& row); + static Value MembersWithStateAccessor(const Value& row); + static Value WorstHostStateAccessor(const Value& row); + static Value NumHostsAccessor(const Value& row); + static Value NumHostsPendingAccessor(const Value& row); + static Value NumHostsUpAccessor(const Value& row); + static Value NumHostsDownAccessor(const Value& row); + static Value NumHostsUnreachAccessor(const Value& row); + static Value NumServicesAccessor(const Value& row); + static Value WorstServiceStateAccessor(const Value& row); + static Value NumServicesPendingAccessor(const Value& row); + static Value NumServicesOkAccessor(const Value& row); + static Value NumServicesWarnAccessor(const Value& row); + static Value NumServicesCritAccessor(const Value& row); + static Value NumServicesUnknownAccessor(const Value& row); + static Value WorstServiceHardStateAccessor(const Value& row); + static Value NumServicesHardOkAccessor(const Value& row); + static Value NumServicesHardWarnAccessor(const Value& row); + static Value NumServicesHardCritAccessor(const Value& row); + static Value NumServicesHardUnknownAccessor(const Value& row); +}; + +} + +#endif /* HOSTGROUPSTABLE_H */ diff --git a/lib/livestatus/hoststable.cpp b/lib/livestatus/hoststable.cpp new file mode 100644 index 0000000..d90f4a5 --- /dev/null +++ b/lib/livestatus/hoststable.cpp @@ -0,0 +1,1517 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/hoststable.hpp" +#include "livestatus/hostgroupstable.hpp" +#include "livestatus/endpointstable.hpp" +#include "icinga/host.hpp" +#include "icinga/service.hpp" +#include "icinga/hostgroup.hpp" +#include "icinga/checkcommand.hpp" +#include "icinga/eventcommand.hpp" +#include "icinga/timeperiod.hpp" +#include "icinga/macroprocessor.hpp" +#include "icinga/compatutility.hpp" +#include "icinga/pluginutility.hpp" +#include "base/configtype.hpp" +#include "base/objectlock.hpp" +#include "base/json.hpp" +#include "base/convert.hpp" +#include "base/utility.hpp" +#include <boost/algorithm/string/replace.hpp> + +using namespace icinga; + +HostsTable::HostsTable(LivestatusGroupByType type) + :Table(type) +{ + AddColumns(this); +} + +void HostsTable::AddColumns(Table *table, const String& prefix, + const Column::ObjectAccessor& objectAccessor) +{ + table->AddColumn(prefix + "name", Column(&HostsTable::NameAccessor, objectAccessor)); + table->AddColumn(prefix + "host_name", Column(&HostsTable::NameAccessor, objectAccessor)); //ugly compatibility hack + table->AddColumn(prefix + "display_name", Column(&HostsTable::DisplayNameAccessor, objectAccessor)); + table->AddColumn(prefix + "alias", Column(&HostsTable::DisplayNameAccessor, objectAccessor)); + table->AddColumn(prefix + "address", Column(&HostsTable::AddressAccessor, objectAccessor)); + table->AddColumn(prefix + "address6", Column(&HostsTable::Address6Accessor, objectAccessor)); + table->AddColumn(prefix + "check_command", Column(&HostsTable::CheckCommandAccessor, objectAccessor)); + table->AddColumn(prefix + "check_command_expanded", Column(&HostsTable::CheckCommandExpandedAccessor, objectAccessor)); + table->AddColumn(prefix + "event_handler", Column(&HostsTable::EventHandlerAccessor, objectAccessor)); + table->AddColumn(prefix + "notification_period", Column(&Table::EmptyStringAccessor, objectAccessor)); + table->AddColumn(prefix + "check_period", Column(&HostsTable::CheckPeriodAccessor, objectAccessor)); + table->AddColumn(prefix + "notes", Column(&HostsTable::NotesAccessor, objectAccessor)); + table->AddColumn(prefix + "notes_expanded", Column(&HostsTable::NotesExpandedAccessor, objectAccessor)); + table->AddColumn(prefix + "notes_url", Column(&HostsTable::NotesUrlAccessor, objectAccessor)); + table->AddColumn(prefix + "notes_url_expanded", Column(&HostsTable::NotesUrlExpandedAccessor, objectAccessor)); + table->AddColumn(prefix + "action_url", Column(&HostsTable::ActionUrlAccessor, objectAccessor)); + table->AddColumn(prefix + "action_url_expanded", Column(&HostsTable::ActionUrlExpandedAccessor, objectAccessor)); + table->AddColumn(prefix + "plugin_output", Column(&HostsTable::PluginOutputAccessor, objectAccessor)); + table->AddColumn(prefix + "perf_data", Column(&HostsTable::PerfDataAccessor, objectAccessor)); + table->AddColumn(prefix + "icon_image", Column(&HostsTable::IconImageAccessor, objectAccessor)); + table->AddColumn(prefix + "icon_image_expanded", Column(&HostsTable::IconImageExpandedAccessor, objectAccessor)); + table->AddColumn(prefix + "icon_image_alt", Column(&HostsTable::IconImageAltAccessor, objectAccessor)); + table->AddColumn(prefix + "statusmap_image", Column(&Table::EmptyStringAccessor, objectAccessor)); + table->AddColumn(prefix + "long_plugin_output", Column(&HostsTable::LongPluginOutputAccessor, objectAccessor)); + table->AddColumn(prefix + "initial_state", Column(&Table::EmptyStringAccessor, objectAccessor)); + table->AddColumn(prefix + "max_check_attempts", Column(&HostsTable::MaxCheckAttemptsAccessor, objectAccessor)); + table->AddColumn(prefix + "flap_detection_enabled", Column(&HostsTable::FlapDetectionEnabledAccessor, objectAccessor)); + table->AddColumn(prefix + "check_freshness", Column(&Table::OneAccessor, objectAccessor)); + table->AddColumn(prefix + "process_performance_data", Column(&HostsTable::ProcessPerformanceDataAccessor, objectAccessor)); + table->AddColumn(prefix + "accept_passive_checks", Column(&HostsTable::AcceptPassiveChecksAccessor, objectAccessor)); + table->AddColumn(prefix + "event_handler_enabled", Column(&HostsTable::EventHandlerEnabledAccessor, objectAccessor)); + table->AddColumn(prefix + "acknowledgement_type", Column(&HostsTable::AcknowledgementTypeAccessor, objectAccessor)); + table->AddColumn(prefix + "check_type", Column(&HostsTable::CheckTypeAccessor, objectAccessor)); + table->AddColumn(prefix + "last_state", Column(&HostsTable::LastStateAccessor, objectAccessor)); + table->AddColumn(prefix + "last_hard_state", Column(&HostsTable::LastHardStateAccessor, objectAccessor)); + table->AddColumn(prefix + "current_attempt", Column(&HostsTable::CurrentAttemptAccessor, objectAccessor)); + table->AddColumn(prefix + "last_notification", Column(&HostsTable::LastNotificationAccessor, objectAccessor)); + table->AddColumn(prefix + "next_notification", Column(&HostsTable::NextNotificationAccessor, objectAccessor)); + table->AddColumn(prefix + "next_check", Column(&HostsTable::NextCheckAccessor, objectAccessor)); + table->AddColumn(prefix + "last_hard_state_change", Column(&HostsTable::LastHardStateChangeAccessor, objectAccessor)); + table->AddColumn(prefix + "has_been_checked", Column(&HostsTable::HasBeenCheckedAccessor, objectAccessor)); + table->AddColumn(prefix + "current_notification_number", Column(&HostsTable::CurrentNotificationNumberAccessor, objectAccessor)); + table->AddColumn(prefix + "pending_flex_downtime", Column(&Table::ZeroAccessor, objectAccessor)); + table->AddColumn(prefix + "total_services", Column(&HostsTable::TotalServicesAccessor, objectAccessor)); + table->AddColumn(prefix + "checks_enabled", Column(&HostsTable::ChecksEnabledAccessor, objectAccessor)); + table->AddColumn(prefix + "notifications_enabled", Column(&HostsTable::NotificationsEnabledAccessor, objectAccessor)); + table->AddColumn(prefix + "acknowledged", Column(&HostsTable::AcknowledgedAccessor, objectAccessor)); + table->AddColumn(prefix + "state", Column(&HostsTable::StateAccessor, objectAccessor)); + table->AddColumn(prefix + "state_type", Column(&HostsTable::StateTypeAccessor, objectAccessor)); + table->AddColumn(prefix + "no_more_notifications", Column(&HostsTable::NoMoreNotificationsAccessor, objectAccessor)); + table->AddColumn(prefix + "check_flapping_recovery_notification", Column(&Table::ZeroAccessor, objectAccessor)); + table->AddColumn(prefix + "last_check", Column(&HostsTable::LastCheckAccessor, objectAccessor)); + table->AddColumn(prefix + "last_state_change", Column(&HostsTable::LastStateChangeAccessor, objectAccessor)); + table->AddColumn(prefix + "last_time_up", Column(&HostsTable::LastTimeUpAccessor, objectAccessor)); + table->AddColumn(prefix + "last_time_down", Column(&HostsTable::LastTimeDownAccessor, objectAccessor)); + table->AddColumn(prefix + "last_time_unreachable", Column(&HostsTable::LastTimeUnreachableAccessor, objectAccessor)); + table->AddColumn(prefix + "is_flapping", Column(&HostsTable::IsFlappingAccessor, objectAccessor)); + table->AddColumn(prefix + "scheduled_downtime_depth", Column(&HostsTable::ScheduledDowntimeDepthAccessor, objectAccessor)); + table->AddColumn(prefix + "is_executing", Column(&Table::ZeroAccessor, objectAccessor)); + table->AddColumn(prefix + "active_checks_enabled", Column(&HostsTable::ActiveChecksEnabledAccessor, objectAccessor)); + table->AddColumn(prefix + "check_options", Column(&Table::EmptyStringAccessor, objectAccessor)); + table->AddColumn(prefix + "obsess_over_host", Column(&Table::ZeroAccessor, objectAccessor)); + table->AddColumn(prefix + "modified_attributes", Column(&Table::ZeroAccessor, objectAccessor)); + table->AddColumn(prefix + "modified_attributes_list", Column(&Table::ZeroAccessor, objectAccessor)); + table->AddColumn(prefix + "check_interval", Column(&HostsTable::CheckIntervalAccessor, objectAccessor)); + table->AddColumn(prefix + "retry_interval", Column(&HostsTable::RetryIntervalAccessor, objectAccessor)); + table->AddColumn(prefix + "notification_interval", Column(&HostsTable::NotificationIntervalAccessor, objectAccessor)); + table->AddColumn(prefix + "first_notification_delay", Column(&Table::EmptyStringAccessor, objectAccessor)); + table->AddColumn(prefix + "low_flap_threshold", Column(&HostsTable::LowFlapThresholdAccessor, objectAccessor)); + table->AddColumn(prefix + "high_flap_threshold", Column(&HostsTable::HighFlapThresholdAccessor, objectAccessor)); + table->AddColumn(prefix + "x_3d", Column(&EmptyStringAccessor, objectAccessor)); + table->AddColumn(prefix + "y_3d", Column(&EmptyStringAccessor, objectAccessor)); + table->AddColumn(prefix + "z_3d", Column(&EmptyStringAccessor, objectAccessor)); + table->AddColumn(prefix + "x_2d", Column(&Table::EmptyStringAccessor, objectAccessor)); + table->AddColumn(prefix + "y_2d", Column(&Table::EmptyStringAccessor, objectAccessor)); + table->AddColumn(prefix + "latency", Column(&HostsTable::LatencyAccessor, objectAccessor)); + table->AddColumn(prefix + "execution_time", Column(&HostsTable::ExecutionTimeAccessor, objectAccessor)); + table->AddColumn(prefix + "percent_state_change", Column(&HostsTable::PercentStateChangeAccessor, objectAccessor)); + table->AddColumn(prefix + "in_notification_period", Column(&HostsTable::InNotificationPeriodAccessor, objectAccessor)); + table->AddColumn(prefix + "in_check_period", Column(&HostsTable::InCheckPeriodAccessor, objectAccessor)); + table->AddColumn(prefix + "contacts", Column(&HostsTable::ContactsAccessor, objectAccessor)); + table->AddColumn(prefix + "downtimes", Column(&HostsTable::DowntimesAccessor, objectAccessor)); + table->AddColumn(prefix + "downtimes_with_info", Column(&HostsTable::DowntimesWithInfoAccessor, objectAccessor)); + table->AddColumn(prefix + "comments", Column(&HostsTable::CommentsAccessor, objectAccessor)); + table->AddColumn(prefix + "comments_with_info", Column(&HostsTable::CommentsWithInfoAccessor, objectAccessor)); + table->AddColumn(prefix + "comments_with_extra_info", Column(&HostsTable::CommentsWithExtraInfoAccessor, objectAccessor)); + table->AddColumn(prefix + "custom_variable_names", Column(&HostsTable::CustomVariableNamesAccessor, objectAccessor)); + table->AddColumn(prefix + "custom_variable_values", Column(&HostsTable::CustomVariableValuesAccessor, objectAccessor)); + table->AddColumn(prefix + "custom_variables", Column(&HostsTable::CustomVariablesAccessor, objectAccessor)); + table->AddColumn(prefix + "filename", Column(&Table::EmptyStringAccessor, objectAccessor)); + table->AddColumn(prefix + "parents", Column(&HostsTable::ParentsAccessor, objectAccessor)); + table->AddColumn(prefix + "childs", Column(&HostsTable::ChildsAccessor, objectAccessor)); + table->AddColumn(prefix + "num_services", Column(&HostsTable::NumServicesAccessor, objectAccessor)); + table->AddColumn(prefix + "worst_service_state", Column(&HostsTable::WorstServiceStateAccessor, objectAccessor)); + table->AddColumn(prefix + "num_services_ok", Column(&HostsTable::NumServicesOkAccessor, objectAccessor)); + table->AddColumn(prefix + "num_services_warn", Column(&HostsTable::NumServicesWarnAccessor, objectAccessor)); + table->AddColumn(prefix + "num_services_crit", Column(&HostsTable::NumServicesCritAccessor, objectAccessor)); + table->AddColumn(prefix + "num_services_unknown", Column(&HostsTable::NumServicesUnknownAccessor, objectAccessor)); + table->AddColumn(prefix + "num_services_pending", Column(&HostsTable::NumServicesPendingAccessor, objectAccessor)); + table->AddColumn(prefix + "worst_service_hard_state", Column(&HostsTable::WorstServiceHardStateAccessor, objectAccessor)); + table->AddColumn(prefix + "num_services_hard_ok", Column(&HostsTable::NumServicesHardOkAccessor, objectAccessor)); + table->AddColumn(prefix + "num_services_hard_warn", Column(&HostsTable::NumServicesHardWarnAccessor, objectAccessor)); + table->AddColumn(prefix + "num_services_hard_crit", Column(&HostsTable::NumServicesHardCritAccessor, objectAccessor)); + table->AddColumn(prefix + "num_services_hard_unknown", Column(&HostsTable::NumServicesHardUnknownAccessor, objectAccessor)); + table->AddColumn(prefix + "hard_state", Column(&HostsTable::HardStateAccessor, objectAccessor)); + table->AddColumn(prefix + "pnpgraph_present", Column(&Table::ZeroAccessor, objectAccessor)); + table->AddColumn(prefix + "staleness", Column(&HostsTable::StalenessAccessor, objectAccessor)); + table->AddColumn(prefix + "groups", Column(&HostsTable::GroupsAccessor, objectAccessor)); + table->AddColumn(prefix + "contact_groups", Column(&HostsTable::ContactGroupsAccessor, objectAccessor)); + table->AddColumn(prefix + "services", Column(&HostsTable::ServicesAccessor, objectAccessor)); + table->AddColumn(prefix + "services_with_state", Column(&HostsTable::ServicesWithStateAccessor, objectAccessor)); + table->AddColumn(prefix + "services_with_info", Column(&HostsTable::ServicesWithInfoAccessor, objectAccessor)); + table->AddColumn(prefix + "check_source", Column(&HostsTable::CheckSourceAccessor, objectAccessor)); + table->AddColumn(prefix + "is_reachable", Column(&HostsTable::IsReachableAccessor, objectAccessor)); + table->AddColumn(prefix + "cv_is_json", Column(&HostsTable::CVIsJsonAccessor, objectAccessor)); + table->AddColumn(prefix + "original_attributes", Column(&HostsTable::OriginalAttributesAccessor, objectAccessor)); + + /* add additional group by values received through the object accessor */ + if (table->GetGroupByType() == LivestatusGroupByHostGroup) { + /* _1 = row, _2 = groupByType, _3 = groupByObject */ + Log(LogDebug, "Livestatus") + << "Processing hosts group by hostgroup table."; + HostGroupsTable::AddColumns(table, "hostgroup_", [](const Value& row, LivestatusGroupByType groupByType, const Object::Ptr& groupByObject) -> Value { + return HostGroupAccessor(row, groupByType, groupByObject); + }); + } +} + +String HostsTable::GetName() const +{ + return "hosts"; +} + +String HostsTable::GetPrefix() const +{ + return "host"; +} + +void HostsTable::FetchRows(const AddRowFunction& addRowFn) +{ + if (GetGroupByType() == LivestatusGroupByHostGroup) { + for (const HostGroup::Ptr& hg : ConfigType::GetObjectsByType<HostGroup>()) { + for (const Host::Ptr& host : hg->GetMembers()) { + /* the caller must know which groupby type and value are set for this row */ + if (!addRowFn(host, LivestatusGroupByHostGroup, hg)) + return; + } + } + } else { + for (const Host::Ptr& host : ConfigType::GetObjectsByType<Host>()) { + if (!addRowFn(host, LivestatusGroupByNone, Empty)) + return; + } + } +} + +Object::Ptr HostsTable::HostGroupAccessor(const Value& row, LivestatusGroupByType groupByType, const Object::Ptr& groupByObject) +{ + /* return the current group by value set from within FetchRows() + * this is the hostgrouo object used for the table join inside + * in AddColumns() + */ + if (groupByType == LivestatusGroupByHostGroup) + return groupByObject; + + return nullptr; +} + +Value HostsTable::NameAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return host->GetName(); +} + +Value HostsTable::DisplayNameAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return host->GetDisplayName(); +} + +Value HostsTable::AddressAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return host->GetAddress(); +} + +Value HostsTable::Address6Accessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return host->GetAddress6(); +} + +Value HostsTable::CheckCommandAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + CheckCommand::Ptr checkcommand = host->GetCheckCommand(); + if (checkcommand) + return CompatUtility::GetCommandName(checkcommand) + "!" + CompatUtility::GetCheckableCommandArgs(host); + + return Empty; +} + +Value HostsTable::CheckCommandExpandedAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + CheckCommand::Ptr checkcommand = host->GetCheckCommand(); + if (checkcommand) + return CompatUtility::GetCommandName(checkcommand) + "!" + CompatUtility::GetCheckableCommandArgs(host); + + return Empty; +} + +Value HostsTable::EventHandlerAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + EventCommand::Ptr eventcommand = host->GetEventCommand(); + if (eventcommand) + return CompatUtility::GetCommandName(eventcommand); + + return Empty; +} + +Value HostsTable::CheckPeriodAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + TimePeriod::Ptr checkPeriod = host->GetCheckPeriod(); + + if (!checkPeriod) + return Empty; + + return checkPeriod->GetName(); +} + +Value HostsTable::NotesAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return host->GetNotes(); +} + +Value HostsTable::NotesExpandedAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + MacroProcessor::ResolverList resolvers { + { "host", host }, + }; + + return MacroProcessor::ResolveMacros(host->GetNotes(), resolvers); +} + +Value HostsTable::NotesUrlAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return host->GetNotesUrl(); +} + +Value HostsTable::NotesUrlExpandedAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + MacroProcessor::ResolverList resolvers { + { "host", host }, + }; + + return MacroProcessor::ResolveMacros(host->GetNotesUrl(), resolvers); +} + +Value HostsTable::ActionUrlAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return host->GetActionUrl(); +} + +Value HostsTable::ActionUrlExpandedAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + MacroProcessor::ResolverList resolvers { + { "host", host }, + }; + + return MacroProcessor::ResolveMacros(host->GetActionUrl(), resolvers); +} + +Value HostsTable::PluginOutputAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + String output; + CheckResult::Ptr cr = host->GetLastCheckResult(); + + if (cr) + output = CompatUtility::GetCheckResultOutput(cr); + + return output; +} + +Value HostsTable::PerfDataAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + String perfdata; + CheckResult::Ptr cr = host->GetLastCheckResult(); + + if (!cr) + return Empty; + + return PluginUtility::FormatPerfdata(cr->GetPerformanceData()); +} + +Value HostsTable::IconImageAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return host->GetIconImage(); +} + +Value HostsTable::IconImageExpandedAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + MacroProcessor::ResolverList resolvers { + { "host", host }, + }; + + return MacroProcessor::ResolveMacros(host->GetIconImage(), resolvers); +} + +Value HostsTable::IconImageAltAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return host->GetIconImageAlt(); +} + +Value HostsTable::LongPluginOutputAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + String long_output; + CheckResult::Ptr cr = host->GetLastCheckResult(); + + if (cr) + long_output = CompatUtility::GetCheckResultLongOutput(cr); + + return long_output; +} + +Value HostsTable::MaxCheckAttemptsAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return host->GetMaxCheckAttempts(); +} + +Value HostsTable::FlapDetectionEnabledAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return Convert::ToLong(host->GetEnableFlapping()); +} + +Value HostsTable::AcceptPassiveChecksAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return Convert::ToLong(host->GetEnablePassiveChecks()); +} + +Value HostsTable::EventHandlerEnabledAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return Convert::ToLong(host->GetEnableEventHandler()); +} + +Value HostsTable::AcknowledgementTypeAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + ObjectLock olock(host); + return host->GetAcknowledgement(); +} + +Value HostsTable::CheckTypeAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return (host->GetEnableActiveChecks() ? 0 : 1); /* 0 .. active, 1 .. passive */ +} + +Value HostsTable::LastStateAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return host->GetLastState(); +} + +Value HostsTable::LastHardStateAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return host->GetLastHardState(); +} + +Value HostsTable::CurrentAttemptAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return host->GetCheckAttempt(); +} + +Value HostsTable::LastNotificationAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return CompatUtility::GetCheckableNotificationLastNotification(host); +} + +Value HostsTable::NextNotificationAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return CompatUtility::GetCheckableNotificationNextNotification(host); +} + +Value HostsTable::NextCheckAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return static_cast<int>(host->GetNextCheck()); +} + +Value HostsTable::LastHardStateChangeAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return static_cast<int>(host->GetLastHardStateChange()); +} + +Value HostsTable::HasBeenCheckedAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return Convert::ToLong(host->HasBeenChecked()); +} + +Value HostsTable::CurrentNotificationNumberAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return CompatUtility::GetCheckableNotificationNotificationNumber(host); +} + +Value HostsTable::TotalServicesAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return host->GetTotalServices(); +} + +Value HostsTable::ChecksEnabledAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return Convert::ToLong(host->GetEnableActiveChecks()); +} + +Value HostsTable::NotificationsEnabledAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return Convert::ToLong(host->GetEnableNotifications()); +} + +Value HostsTable::ProcessPerformanceDataAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return Convert::ToLong(host->GetEnablePerfdata()); +} + +Value HostsTable::AcknowledgedAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + ObjectLock olock(host); + return host->IsAcknowledged(); +} + +Value HostsTable::StateAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return host->IsReachable() ? host->GetState() : 2; +} + +Value HostsTable::StateTypeAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return host->GetStateType(); +} + +Value HostsTable::NoMoreNotificationsAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return (CompatUtility::GetCheckableNotificationNotificationInterval(host) == 0 && !host->GetVolatile()) ? 1 : 0; +} + +Value HostsTable::LastCheckAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return static_cast<int>(host->GetLastCheck()); +} + +Value HostsTable::LastStateChangeAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return static_cast<int>(host->GetLastStateChange()); +} + +Value HostsTable::LastTimeUpAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return static_cast<int>(host->GetLastStateUp()); +} + +Value HostsTable::LastTimeDownAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return static_cast<int>(host->GetLastStateDown()); +} + +Value HostsTable::LastTimeUnreachableAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return static_cast<int>(host->GetLastStateUnreachable()); +} + +Value HostsTable::IsFlappingAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return host->IsFlapping(); +} + +Value HostsTable::ScheduledDowntimeDepthAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return host->GetDowntimeDepth(); +} + +Value HostsTable::ActiveChecksEnabledAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return Convert::ToLong(host->GetEnableActiveChecks()); +} + +Value HostsTable::CheckIntervalAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return host->GetCheckInterval() / LIVESTATUS_INTERVAL_LENGTH; +} + +Value HostsTable::RetryIntervalAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return host->GetRetryInterval() / LIVESTATUS_INTERVAL_LENGTH; +} + +Value HostsTable::NotificationIntervalAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return CompatUtility::GetCheckableNotificationNotificationInterval(host); +} + +Value HostsTable::LowFlapThresholdAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return host->GetFlappingThresholdLow(); +} + +Value HostsTable::HighFlapThresholdAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return host->GetFlappingThresholdHigh(); +} + +Value HostsTable::LatencyAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + CheckResult::Ptr cr = host->GetLastCheckResult(); + + if (!cr) + return Empty; + + return cr->CalculateLatency(); +} + +Value HostsTable::ExecutionTimeAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + CheckResult::Ptr cr = host->GetLastCheckResult(); + + if (!cr) + return Empty; + + return cr->CalculateExecutionTime(); +} + +Value HostsTable::PercentStateChangeAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return host->GetFlappingCurrent(); +} + +Value HostsTable::InNotificationPeriodAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + for (const Notification::Ptr& notification : host->GetNotifications()) { + TimePeriod::Ptr timeperiod = notification->GetPeriod(); + + if (!timeperiod || timeperiod->IsInside(Utility::GetTime())) + return 1; + } + + return 0; +} + +Value HostsTable::InCheckPeriodAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + TimePeriod::Ptr timeperiod = host->GetCheckPeriod(); + + /* none set means always checked */ + if (!timeperiod) + return 1; + + return Convert::ToLong(timeperiod->IsInside(Utility::GetTime())); +} + +Value HostsTable::ContactsAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + ArrayData result; + + for (const User::Ptr& user : CompatUtility::GetCheckableNotificationUsers(host)) { + result.push_back(user->GetName()); + } + + return new Array(std::move(result)); +} + +Value HostsTable::DowntimesAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + ArrayData result; + + for (const Downtime::Ptr& downtime : host->GetDowntimes()) { + if (downtime->IsExpired()) + continue; + + result.push_back(downtime->GetLegacyId()); + } + + return new Array(std::move(result)); +} + +Value HostsTable::DowntimesWithInfoAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + ArrayData result; + + for (const Downtime::Ptr& downtime : host->GetDowntimes()) { + if (downtime->IsExpired()) + continue; + + result.push_back(new Array({ + downtime->GetLegacyId(), + downtime->GetAuthor(), + downtime->GetComment() + })); + } + + return new Array(std::move(result)); +} + +Value HostsTable::CommentsAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + ArrayData result; + + for (const Comment::Ptr& comment : host->GetComments()) { + if (comment->IsExpired()) + continue; + + result.push_back(comment->GetLegacyId()); + } + + return new Array(std::move(result)); +} + +Value HostsTable::CommentsWithInfoAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + ArrayData result; + + for (const Comment::Ptr& comment : host->GetComments()) { + if (comment->IsExpired()) + continue; + + result.push_back(new Array({ + comment->GetLegacyId(), + comment->GetAuthor(), + comment->GetText() + })); + } + + return new Array(std::move(result)); +} + +Value HostsTable::CommentsWithExtraInfoAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + ArrayData result; + + for (const Comment::Ptr& comment : host->GetComments()) { + if (comment->IsExpired()) + continue; + + result.push_back(new Array({ + comment->GetLegacyId(), + comment->GetAuthor(), + comment->GetText(), + comment->GetEntryType(), + static_cast<int>(comment->GetEntryTime()) + })); + } + + return new Array(std::move(result)); +} + +Value HostsTable::CustomVariableNamesAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + Dictionary::Ptr vars = host->GetVars(); + + ArrayData result; + + if (vars) { + ObjectLock olock(vars); + for (const Dictionary::Pair& kv : vars) { + result.push_back(kv.first); + } + } + + return new Array(std::move(result)); +} + +Value HostsTable::CustomVariableValuesAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + Dictionary::Ptr vars = host->GetVars(); + + ArrayData result; + + if (vars) { + ObjectLock olock(vars); + for (const Dictionary::Pair& kv : vars) { + if (kv.second.IsObjectType<Array>() || kv.second.IsObjectType<Dictionary>()) + result.push_back(JsonEncode(kv.second)); + else + result.push_back(kv.second); + } + } + + return new Array(std::move(result)); +} + +Value HostsTable::CustomVariablesAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + Dictionary::Ptr vars = host->GetVars(); + + ArrayData result; + + if (vars) { + ObjectLock olock(vars); + for (const Dictionary::Pair& kv : vars) { + Value val; + + if (kv.second.IsObjectType<Array>() || kv.second.IsObjectType<Dictionary>()) + val = JsonEncode(kv.second); + else + val = kv.second; + + result.push_back(new Array({ + kv.first, + val + })); + } + } + + return new Array(std::move(result)); +} + +Value HostsTable::CVIsJsonAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + Dictionary::Ptr vars = host->GetVars(); + + if (!vars) + return Empty; + + bool cv_is_json = false; + + ObjectLock olock(vars); + for (const Dictionary::Pair& kv : vars) { + if (kv.second.IsObjectType<Array>() || kv.second.IsObjectType<Dictionary>()) + cv_is_json = true; + } + + return cv_is_json; +} + +Value HostsTable::ParentsAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + ArrayData result; + + for (const Checkable::Ptr& parent : host->GetParents()) { + Host::Ptr parent_host = dynamic_pointer_cast<Host>(parent); + + if (!parent_host) + continue; + + result.push_back(parent_host->GetName()); + } + + return new Array(std::move(result)); +} + +Value HostsTable::ChildsAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + ArrayData result; + + for (const Checkable::Ptr& child : host->GetChildren()) { + Host::Ptr child_host = dynamic_pointer_cast<Host>(child); + + if (!child_host) + continue; + + result.push_back(child_host->GetName()); + } + + return new Array(std::move(result)); +} + +Value HostsTable::NumServicesAccessor(const Value& row) +{ + /* duplicate of TotalServices */ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return host->GetTotalServices(); +} + +Value HostsTable::WorstServiceStateAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + Value worst_service = ServiceOK; + + for (const Service::Ptr& service : host->GetServices()) { + if (service->GetState() > worst_service) + worst_service = service->GetState(); + } + + return worst_service; +} + +Value HostsTable::NumServicesOkAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + int num_services = 0; + + for (const Service::Ptr& service : host->GetServices()) { + if (service->GetState() == ServiceOK) + num_services++; + } + + return num_services; +} + +Value HostsTable::NumServicesWarnAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + int num_services = 0; + + for (const Service::Ptr& service : host->GetServices()) { + if (service->GetState() == ServiceWarning) + num_services++; + } + + return num_services; +} + +Value HostsTable::NumServicesCritAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + int num_services = 0; + + for (const Service::Ptr& service : host->GetServices()) { + if (service->GetState() == ServiceCritical) + num_services++; + } + + return num_services; +} + +Value HostsTable::NumServicesUnknownAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + int num_services = 0; + + for (const Service::Ptr& service : host->GetServices()) { + if (service->GetState() == ServiceUnknown) + num_services++; + } + + return num_services; +} + +Value HostsTable::NumServicesPendingAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + int num_services = 0; + + for (const Service::Ptr& service : host->GetServices()) { + if (!service->GetLastCheckResult()) + num_services++; + } + + return num_services; +} + +Value HostsTable::WorstServiceHardStateAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + Value worst_service = ServiceOK; + + for (const Service::Ptr& service : host->GetServices()) { + if (service->GetStateType() == StateTypeHard) { + if (service->GetState() > worst_service) + worst_service = service->GetState(); + } + } + + return worst_service; +} + +Value HostsTable::NumServicesHardOkAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + int num_services = 0; + + for (const Service::Ptr& service : host->GetServices()) { + if (service->GetStateType() == StateTypeHard && service->GetState() == ServiceOK) + num_services++; + } + + return num_services; +} + +Value HostsTable::NumServicesHardWarnAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + int num_services = 0; + + for (const Service::Ptr& service : host->GetServices()) { + if (service->GetStateType() == StateTypeHard && service->GetState() == ServiceWarning) + num_services++; + } + + return num_services; +} + +Value HostsTable::NumServicesHardCritAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + int num_services = 0; + + for (const Service::Ptr& service : host->GetServices()) { + if (service->GetStateType() == StateTypeHard && service->GetState() == ServiceCritical) + num_services++; + } + + return num_services; +} + +Value HostsTable::NumServicesHardUnknownAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + int num_services = 0; + + for (const Service::Ptr& service : host->GetServices()) { + if (service->GetStateType() == StateTypeHard && service->GetState() == ServiceUnknown) + num_services++; + } + + return num_services; +} + +Value HostsTable::HardStateAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + if (host->GetState() == HostUp) + return HostUp; + else if (host->GetStateType() == StateTypeHard) + return host->GetState(); + + return host->GetLastHardState(); +} + +Value HostsTable::StalenessAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + if (host->HasBeenChecked() && host->GetLastCheck() > 0) + return (Utility::GetTime() - host->GetLastCheck()) / (host->GetCheckInterval() * 3600); + + return 0.0; +} + +Value HostsTable::GroupsAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + Array::Ptr groups = host->GetGroups(); + + if (!groups) + return Empty; + + return groups; +} + +Value HostsTable::ContactGroupsAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + ArrayData result; + + for (const UserGroup::Ptr& usergroup : CompatUtility::GetCheckableNotificationUserGroups(host)) { + result.push_back(usergroup->GetName()); + } + + return new Array(std::move(result)); +} + +Value HostsTable::ServicesAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + std::vector<Service::Ptr> rservices = host->GetServices(); + + ArrayData result; + result.reserve(rservices.size()); + + for (const Service::Ptr& service : rservices) { + result.push_back(service->GetShortName()); + } + + return new Array(std::move(result)); +} + +Value HostsTable::ServicesWithStateAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + std::vector<Service::Ptr> rservices = host->GetServices(); + + ArrayData result; + result.reserve(rservices.size()); + + for (const Service::Ptr& service : rservices) { + result.push_back(new Array({ + service->GetShortName(), + service->GetState(), + service->HasBeenChecked() ? 1 : 0 + })); + } + + return new Array(std::move(result)); +} + +Value HostsTable::ServicesWithInfoAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + std::vector<Service::Ptr> rservices = host->GetServices(); + + ArrayData result; + result.reserve(rservices.size()); + + for (const Service::Ptr& service : rservices) { + String output; + CheckResult::Ptr cr = service->GetLastCheckResult(); + + if (cr) + output = CompatUtility::GetCheckResultOutput(cr); + + result.push_back(new Array({ + service->GetShortName(), + service->GetState(), + service->HasBeenChecked() ? 1 : 0, + output + })); + } + + return new Array(std::move(result)); +} + +Value HostsTable::CheckSourceAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + CheckResult::Ptr cr = host->GetLastCheckResult(); + + if (cr) + return cr->GetCheckSource(); + + return Empty; +} + +Value HostsTable::IsReachableAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return host->IsReachable(); +} + +Value HostsTable::OriginalAttributesAccessor(const Value& row) +{ + Host::Ptr host = static_cast<Host::Ptr>(row); + + if (!host) + return Empty; + + return JsonEncode(host->GetOriginalAttributes()); +} diff --git a/lib/livestatus/hoststable.hpp b/lib/livestatus/hoststable.hpp new file mode 100644 index 0000000..9386183 --- /dev/null +++ b/lib/livestatus/hoststable.hpp @@ -0,0 +1,133 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef HOSTSTABLE_H +#define HOSTSTABLE_H + +#include "livestatus/table.hpp" + +using namespace icinga; + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +class HostsTable final : public Table +{ +public: + DECLARE_PTR_TYPEDEFS(HostsTable); + + HostsTable(LivestatusGroupByType type = LivestatusGroupByNone); + + static void AddColumns(Table *table, const String& prefix = String(), + const Column::ObjectAccessor& objectAccessor = Column::ObjectAccessor()); + + String GetName() const override; + String GetPrefix() const override; + +protected: + void FetchRows(const AddRowFunction& addRowFn) override; + + static Object::Ptr HostGroupAccessor(const Value& row, LivestatusGroupByType groupByType, const Object::Ptr& groupByObject); + + static Value NameAccessor(const Value& row); + static Value DisplayNameAccessor(const Value& row); + static Value AddressAccessor(const Value& row); + static Value Address6Accessor(const Value& row); + static Value CheckCommandAccessor(const Value& row); + static Value CheckCommandExpandedAccessor(const Value& row); + static Value EventHandlerAccessor(const Value& row); + static Value CheckPeriodAccessor(const Value& row); + static Value NotesAccessor(const Value& row); + static Value NotesExpandedAccessor(const Value& row); + static Value NotesUrlAccessor(const Value& row); + static Value NotesUrlExpandedAccessor(const Value& row); + static Value ActionUrlAccessor(const Value& row); + static Value ActionUrlExpandedAccessor(const Value& row); + static Value PluginOutputAccessor(const Value& row); + static Value PerfDataAccessor(const Value& row); + static Value IconImageAccessor(const Value& row); + static Value IconImageExpandedAccessor(const Value& row); + static Value IconImageAltAccessor(const Value& row); + static Value LongPluginOutputAccessor(const Value& row); + static Value MaxCheckAttemptsAccessor(const Value& row); + static Value FlapDetectionEnabledAccessor(const Value& row); + static Value ProcessPerformanceDataAccessor(const Value& row); + static Value AcceptPassiveChecksAccessor(const Value& row); + static Value EventHandlerEnabledAccessor(const Value& row); + static Value AcknowledgementTypeAccessor(const Value& row); + static Value CheckTypeAccessor(const Value& row); + static Value LastStateAccessor(const Value& row); + static Value LastHardStateAccessor(const Value& row); + static Value CurrentAttemptAccessor(const Value& row); + static Value LastNotificationAccessor(const Value& row); + static Value NextNotificationAccessor(const Value& row); + static Value NextCheckAccessor(const Value& row); + static Value LastHardStateChangeAccessor(const Value& row); + static Value HasBeenCheckedAccessor(const Value& row); + static Value CurrentNotificationNumberAccessor(const Value& row); + static Value TotalServicesAccessor(const Value& row); + static Value ChecksEnabledAccessor(const Value& row); + static Value NotificationsEnabledAccessor(const Value& row); + static Value AcknowledgedAccessor(const Value& row); + static Value StateAccessor(const Value& row); + static Value StateTypeAccessor(const Value& row); + static Value NoMoreNotificationsAccessor(const Value& row); + static Value LastCheckAccessor(const Value& row); + static Value LastStateChangeAccessor(const Value& row); + static Value LastTimeUpAccessor(const Value& row); + static Value LastTimeDownAccessor(const Value& row); + static Value LastTimeUnreachableAccessor(const Value& row); + static Value IsFlappingAccessor(const Value& row); + static Value ScheduledDowntimeDepthAccessor(const Value& row); + static Value ActiveChecksEnabledAccessor(const Value& row); + static Value CheckIntervalAccessor(const Value& row); + static Value RetryIntervalAccessor(const Value& row); + static Value NotificationIntervalAccessor(const Value& row); + static Value LowFlapThresholdAccessor(const Value& row); + static Value HighFlapThresholdAccessor(const Value& row); + static Value LatencyAccessor(const Value& row); + static Value ExecutionTimeAccessor(const Value& row); + static Value PercentStateChangeAccessor(const Value& row); + static Value InNotificationPeriodAccessor(const Value& row); + static Value InCheckPeriodAccessor(const Value& row); + static Value ContactsAccessor(const Value& row); + static Value DowntimesAccessor(const Value& row); + static Value DowntimesWithInfoAccessor(const Value& row); + static Value CommentsAccessor(const Value& row); + static Value CommentsWithInfoAccessor(const Value& row); + static Value CommentsWithExtraInfoAccessor(const Value& row); + static Value CustomVariableNamesAccessor(const Value& row); + static Value CustomVariableValuesAccessor(const Value& row); + static Value CustomVariablesAccessor(const Value& row); + static Value ParentsAccessor(const Value& row); + static Value ChildsAccessor(const Value& row); + static Value NumServicesAccessor(const Value& row); + static Value WorstServiceStateAccessor(const Value& row); + static Value NumServicesOkAccessor(const Value& row); + static Value NumServicesWarnAccessor(const Value& row); + static Value NumServicesCritAccessor(const Value& row); + static Value NumServicesUnknownAccessor(const Value& row); + static Value NumServicesPendingAccessor(const Value& row); + static Value WorstServiceHardStateAccessor(const Value& row); + static Value NumServicesHardOkAccessor(const Value& row); + static Value NumServicesHardWarnAccessor(const Value& row); + static Value NumServicesHardCritAccessor(const Value& row); + static Value NumServicesHardUnknownAccessor(const Value& row); + static Value HardStateAccessor(const Value& row); + static Value StalenessAccessor(const Value& row); + static Value GroupsAccessor(const Value& row); + static Value ContactGroupsAccessor(const Value& row); + static Value ServicesAccessor(const Value& row); + static Value ServicesWithStateAccessor(const Value& row); + static Value ServicesWithInfoAccessor(const Value& row); + static Value CheckSourceAccessor(const Value& row); + static Value IsReachableAccessor(const Value& row); + static Value CVIsJsonAccessor(const Value& row); + static Value OriginalAttributesAccessor(const Value& row); +}; + +} + +#endif /* HOSTSTABLE_H */ diff --git a/lib/livestatus/i2-livestatus.hpp b/lib/livestatus/i2-livestatus.hpp new file mode 100644 index 0000000..3375d97 --- /dev/null +++ b/lib/livestatus/i2-livestatus.hpp @@ -0,0 +1,14 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef I2LIVESTATUS_H +#define I2LIVESTATUS_H + +/** + * @defgroup icinga Livestatus + * + * The Livestatus library implements the Livestatus protocol for Icinga. + */ + +#include "base/i2-base.hpp" + +#endif /* I2LIVESTATUS_H */ diff --git a/lib/livestatus/invavgaggregator.cpp b/lib/livestatus/invavgaggregator.cpp new file mode 100644 index 0000000..33cf85c --- /dev/null +++ b/lib/livestatus/invavgaggregator.cpp @@ -0,0 +1,38 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/invavgaggregator.hpp" + +using namespace icinga; + +InvAvgAggregator::InvAvgAggregator(String attr) + : m_InvAvgAttr(std::move(attr)) +{ } + +InvAvgAggregatorState *InvAvgAggregator::EnsureState(AggregatorState **state) +{ + if (!*state) + *state = new InvAvgAggregatorState(); + + return static_cast<InvAvgAggregatorState *>(*state); +} + +void InvAvgAggregator::Apply(const Table::Ptr& table, const Value& row, AggregatorState **state) +{ + Column column = table->GetColumn(m_InvAvgAttr); + + Value value = column.ExtractValue(row); + + InvAvgAggregatorState *pstate = EnsureState(state); + + pstate->InvAvg += (1.0 / value); + pstate->InvAvgCount++; +} + +double InvAvgAggregator::GetResultAndFreeState(AggregatorState *state) const +{ + InvAvgAggregatorState *pstate = EnsureState(&state); + double result = pstate->InvAvg / pstate->InvAvgCount; + delete pstate; + + return result; +} diff --git a/lib/livestatus/invavgaggregator.hpp b/lib/livestatus/invavgaggregator.hpp new file mode 100644 index 0000000..9282b37 --- /dev/null +++ b/lib/livestatus/invavgaggregator.hpp @@ -0,0 +1,42 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef INVAVGAGGREGATOR_H +#define INVAVGAGGREGATOR_H + +#include "livestatus/table.hpp" +#include "livestatus/aggregator.hpp" + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +struct InvAvgAggregatorState final : public AggregatorState +{ + double InvAvg{0}; + double InvAvgCount{0}; +}; + +/** + * @ingroup livestatus + */ +class InvAvgAggregator final : public Aggregator +{ +public: + DECLARE_PTR_TYPEDEFS(InvAvgAggregator); + + InvAvgAggregator(String attr); + + void Apply(const Table::Ptr& table, const Value& row, AggregatorState **state) override; + double GetResultAndFreeState(AggregatorState *state) const override; + +private: + String m_InvAvgAttr; + + static InvAvgAggregatorState *EnsureState(AggregatorState **state); +}; + +} + +#endif /* INVAVGAGGREGATOR_H */ diff --git a/lib/livestatus/invsumaggregator.cpp b/lib/livestatus/invsumaggregator.cpp new file mode 100644 index 0000000..c955667 --- /dev/null +++ b/lib/livestatus/invsumaggregator.cpp @@ -0,0 +1,37 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/invsumaggregator.hpp" + +using namespace icinga; + +InvSumAggregator::InvSumAggregator(String attr) + : m_InvSumAttr(std::move(attr)) +{ } + +InvSumAggregatorState *InvSumAggregator::EnsureState(AggregatorState **state) +{ + if (!*state) + *state = new InvSumAggregatorState(); + + return static_cast<InvSumAggregatorState *>(*state); +} + +void InvSumAggregator::Apply(const Table::Ptr& table, const Value& row, AggregatorState **state) +{ + Column column = table->GetColumn(m_InvSumAttr); + + Value value = column.ExtractValue(row); + + InvSumAggregatorState *pstate = EnsureState(state); + + pstate->InvSum += (1.0 / value); +} + +double InvSumAggregator::GetResultAndFreeState(AggregatorState *state) const +{ + InvSumAggregatorState *pstate = EnsureState(&state); + double result = pstate->InvSum; + delete pstate; + + return result; +} diff --git a/lib/livestatus/invsumaggregator.hpp b/lib/livestatus/invsumaggregator.hpp new file mode 100644 index 0000000..f7de7be --- /dev/null +++ b/lib/livestatus/invsumaggregator.hpp @@ -0,0 +1,41 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef INVSUMAGGREGATOR_H +#define INVSUMAGGREGATOR_H + +#include "livestatus/table.hpp" +#include "livestatus/aggregator.hpp" + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +struct InvSumAggregatorState final : public AggregatorState +{ + double InvSum{0}; +}; + +/** + * @ingroup livestatus + */ +class InvSumAggregator final : public Aggregator +{ +public: + DECLARE_PTR_TYPEDEFS(InvSumAggregator); + + InvSumAggregator(String attr); + + void Apply(const Table::Ptr& table, const Value& row, AggregatorState **state) override; + double GetResultAndFreeState(AggregatorState *state) const override; + +private: + String m_InvSumAttr; + + static InvSumAggregatorState *EnsureState(AggregatorState **state); +}; + +} + +#endif /* INVSUMAGGREGATOR_H */ diff --git a/lib/livestatus/livestatuslistener.cpp b/lib/livestatus/livestatuslistener.cpp new file mode 100644 index 0000000..e44650b --- /dev/null +++ b/lib/livestatus/livestatuslistener.cpp @@ -0,0 +1,211 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/livestatuslistener.hpp" +#include "livestatus/livestatuslistener-ti.cpp" +#include "base/utility.hpp" +#include "base/perfdatavalue.hpp" +#include "base/objectlock.hpp" +#include "base/configtype.hpp" +#include "base/logger.hpp" +#include "base/exception.hpp" +#include "base/tcpsocket.hpp" +#include "base/unixsocket.hpp" +#include "base/networkstream.hpp" +#include "base/application.hpp" +#include "base/function.hpp" +#include "base/statsfunction.hpp" +#include "base/convert.hpp" + +using namespace icinga; + +REGISTER_TYPE(LivestatusListener); + +static int l_ClientsConnected = 0; +static int l_Connections = 0; +static std::mutex l_ComponentMutex; + +REGISTER_STATSFUNCTION(LivestatusListener, &LivestatusListener::StatsFunc); + +void LivestatusListener::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata) +{ + DictionaryData nodes; + + for (const LivestatusListener::Ptr& livestatuslistener : ConfigType::GetObjectsByType<LivestatusListener>()) { + nodes.emplace_back(livestatuslistener->GetName(), new Dictionary({ + { "connections", l_Connections } + })); + + perfdata->Add(new PerfdataValue("livestatuslistener_" + livestatuslistener->GetName() + "_connections", l_Connections)); + } + + status->Set("livestatuslistener", new Dictionary(std::move(nodes))); +} + +/** + * Starts the component. + */ +void LivestatusListener::Start(bool runtimeCreated) +{ + ObjectImpl<LivestatusListener>::Start(runtimeCreated); + + Log(LogInformation, "LivestatusListener") + << "'" << GetName() << "' started."; + + if (GetSocketType() == "tcp") { + TcpSocket::Ptr socket = new TcpSocket(); + + try { + socket->Bind(GetBindHost(), GetBindPort(), AF_UNSPEC); + } catch (std::exception&) { + Log(LogCritical, "LivestatusListener") + << "Cannot bind TCP socket on host '" << GetBindHost() << "' port '" << GetBindPort() << "'."; + return; + } + + m_Listener = socket; + + m_Thread = std::thread([this]() { ServerThreadProc(); }); + + Log(LogInformation, "LivestatusListener") + << "Created TCP socket listening on host '" << GetBindHost() << "' port '" << GetBindPort() << "'."; + } + else if (GetSocketType() == "unix") { +#ifndef _WIN32 + UnixSocket::Ptr socket = new UnixSocket(); + + try { + socket->Bind(GetSocketPath()); + } catch (std::exception&) { + Log(LogCritical, "LivestatusListener") + << "Cannot bind UNIX socket to '" << GetSocketPath() << "'."; + return; + } + + /* group must be able to write */ + mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; + + if (chmod(GetSocketPath().CStr(), mode) < 0) { + Log(LogCritical, "LivestatusListener") + << "chmod() on unix socket '" << GetSocketPath() << "' failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\""; + return; + } + + m_Listener = socket; + + m_Thread = std::thread([this]() { ServerThreadProc(); }); + + Log(LogInformation, "LivestatusListener") + << "Created UNIX socket in '" << GetSocketPath() << "'."; +#else + /* no UNIX sockets on windows */ + Log(LogCritical, "LivestatusListener", "Unix sockets are not supported on Windows."); + return; +#endif + } +} + +void LivestatusListener::Stop(bool runtimeRemoved) +{ + ObjectImpl<LivestatusListener>::Stop(runtimeRemoved); + + Log(LogInformation, "LivestatusListener") + << "'" << GetName() << "' stopped."; + + m_Listener->Close(); + + if (m_Thread.joinable()) + m_Thread.join(); +} + +int LivestatusListener::GetClientsConnected() +{ + std::unique_lock<std::mutex> lock(l_ComponentMutex); + + return l_ClientsConnected; +} + +int LivestatusListener::GetConnections() +{ + std::unique_lock<std::mutex> lock(l_ComponentMutex); + + return l_Connections; +} + +void LivestatusListener::ServerThreadProc() +{ + m_Listener->Listen(); + + try { + for (;;) { + timeval tv = { 0, 500000 }; + + if (m_Listener->Poll(true, false, &tv)) { + Socket::Ptr client = m_Listener->Accept(); + Log(LogNotice, "LivestatusListener", "Client connected"); + Utility::QueueAsyncCallback([this, client]() { ClientHandler(client); }, LowLatencyScheduler); + } + + if (!IsActive()) + break; + } + } catch (std::exception&) { + Log(LogCritical, "LivestatusListener", "Cannot accept new connection."); + } + + m_Listener->Close(); +} + +void LivestatusListener::ClientHandler(const Socket::Ptr& client) +{ + { + std::unique_lock<std::mutex> lock(l_ComponentMutex); + l_ClientsConnected++; + l_Connections++; + } + + Stream::Ptr stream = new NetworkStream(client); + + StreamReadContext context; + + for (;;) { + String line; + + std::vector<String> lines; + + for (;;) { + StreamReadStatus srs = stream->ReadLine(&line, context); + + if (srs == StatusEof) + break; + + if (srs != StatusNewItem) + continue; + + if (line.GetLength() > 0) + lines.push_back(line); + else + break; + } + + if (lines.empty()) + break; + + LivestatusQuery::Ptr query = new LivestatusQuery(lines, GetCompatLogPath()); + if (!query->Execute(stream)) + break; + } + + { + std::unique_lock<std::mutex> lock(l_ComponentMutex); + l_ClientsConnected--; + } +} + + +void LivestatusListener::ValidateSocketType(const Lazy<String>& lvalue, const ValidationUtils& utils) +{ + ObjectImpl<LivestatusListener>::ValidateSocketType(lvalue, utils); + + if (lvalue() != "unix" && lvalue() != "tcp") + BOOST_THROW_EXCEPTION(ValidationError(this, { "socket_type" }, "Socket type '" + lvalue() + "' is invalid.")); +} diff --git a/lib/livestatus/livestatuslistener.hpp b/lib/livestatus/livestatuslistener.hpp new file mode 100644 index 0000000..dc739f6 --- /dev/null +++ b/lib/livestatus/livestatuslistener.hpp @@ -0,0 +1,47 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef LIVESTATUSLISTENER_H +#define LIVESTATUSLISTENER_H + +#include "livestatus/i2-livestatus.hpp" +#include "livestatus/livestatuslistener-ti.hpp" +#include "livestatus/livestatusquery.hpp" +#include "base/socket.hpp" +#include <thread> + +using namespace icinga; + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +class LivestatusListener final : public ObjectImpl<LivestatusListener> +{ +public: + DECLARE_OBJECT(LivestatusListener); + DECLARE_OBJECTNAME(LivestatusListener); + + static void StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata); + + static int GetClientsConnected(); + static int GetConnections(); + + void ValidateSocketType(const Lazy<String>& lvalue, const ValidationUtils& utils) override; + +protected: + void Start(bool runtimeCreated) override; + void Stop(bool runtimeRemoved) override; + +private: + void ServerThreadProc(); + void ClientHandler(const Socket::Ptr& client); + + Socket::Ptr m_Listener; + std::thread m_Thread; +}; + +} + +#endif /* LIVESTATUSLISTENER_H */ diff --git a/lib/livestatus/livestatuslistener.ti b/lib/livestatus/livestatuslistener.ti new file mode 100644 index 0000000..31482cf --- /dev/null +++ b/lib/livestatus/livestatuslistener.ti @@ -0,0 +1,31 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "base/configobject.hpp" +#include "base/application.hpp" + +library livestatus; + +namespace icinga +{ + +class LivestatusListener : ConfigObject { + activation_priority 100; + + [config] String socket_type { + default {{{ return "unix"; }}} + }; + [config] String socket_path { + default {{{ return Configuration::InitRunDir + "/cmd/livestatus"; }}} + }; + [config] String bind_host { + default {{{ return "127.0.0.1"; }}} + }; + [config] String bind_port { + default {{{ return "6558"; }}} + }; + [config] String compat_log_path { + default {{{ return Configuration::LogDir + "/compat"; }}} + }; +}; + +} diff --git a/lib/livestatus/livestatuslogutility.cpp b/lib/livestatus/livestatuslogutility.cpp new file mode 100644 index 0000000..565c2ca --- /dev/null +++ b/lib/livestatus/livestatuslogutility.cpp @@ -0,0 +1,321 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/livestatuslogutility.hpp" +#include "icinga/service.hpp" +#include "icinga/host.hpp" +#include "icinga/user.hpp" +#include "icinga/checkcommand.hpp" +#include "icinga/eventcommand.hpp" +#include "icinga/notificationcommand.hpp" +#include "base/utility.hpp" +#include "base/convert.hpp" +#include "base/logger.hpp" +#include <boost/algorithm/string.hpp> +#include <boost/algorithm/string/replace.hpp> +#include <boost/algorithm/string/predicate.hpp> +#include <fstream> + +using namespace icinga; + +void LivestatusLogUtility::CreateLogIndex(const String& path, std::map<time_t, String>& index) +{ + Utility::Glob(path + "/icinga.log", [&index](const String& newPath) { CreateLogIndexFileHandler(newPath, index); }, GlobFile); + Utility::Glob(path + "/archives/*.log", [&index](const String& newPath) { CreateLogIndexFileHandler(newPath, index); }, GlobFile); +} + +void LivestatusLogUtility::CreateLogIndexFileHandler(const String& path, std::map<time_t, String>& index) +{ + std::ifstream stream; + stream.open(path.CStr(), std::ifstream::in); + + if (!stream) + BOOST_THROW_EXCEPTION(std::runtime_error("Could not open log file: " + path)); + + /* read the first bytes to get the timestamp: [123456789] */ + char buffer[12]; + + stream.read(buffer, 12); + + if (buffer[0] != '[' || buffer[11] != ']') { + /* this can happen for directories too, silently ignore them */ + return; + } + + /* extract timestamp */ + buffer[11] = 0; + time_t ts_start = atoi(buffer+1); + + stream.close(); + + Log(LogDebug, "LivestatusLogUtility") + << "Indexing log file: '" << path << "' with timestamp start: '" << ts_start << "'."; + + index[ts_start] = path; +} + +void LivestatusLogUtility::CreateLogCache(std::map<time_t, String> index, HistoryTable *table, + time_t from, time_t until, const AddRowFunction& addRowFn) +{ + ASSERT(table); + + /* m_LogFileIndex map tells which log files are involved ordered by their start timestamp */ + unsigned long line_count = 0; + for (const auto& kv : index) { + unsigned int ts = kv.first; + + /* skip log files not in range (performance optimization) */ + if (ts < from || ts > until) + continue; + + String log_file = index[ts]; + int lineno = 0; + + std::ifstream fp; + fp.exceptions(std::ifstream::badbit); + fp.open(log_file.CStr(), std::ifstream::in); + + while (fp.good()) { + std::string line; + std::getline(fp, line); + + if (line.empty()) + continue; /* Ignore empty lines */ + + Dictionary::Ptr log_entry_attrs = LivestatusLogUtility::GetAttributes(line); + + /* no attributes available - invalid log line */ + if (!log_entry_attrs) { + Log(LogDebug, "LivestatusLogUtility") + << "Skipping invalid log line: '" << line << "'."; + continue; + } + + table->UpdateLogEntries(log_entry_attrs, line_count, lineno, addRowFn); + + line_count++; + lineno++; + } + + fp.close(); + } +} + +Dictionary::Ptr LivestatusLogUtility::GetAttributes(const String& text) +{ + Dictionary::Ptr bag = new Dictionary(); + + /* + * [1379025342] SERVICE NOTIFICATION: contactname;hostname;servicedesc;WARNING;true;foo output + */ + unsigned long time = atoi(text.SubStr(1, 11).CStr()); + + Log(LogDebug, "LivestatusLogUtility") + << "Processing log line: '" << text << "'."; + bag->Set("time", time); + + size_t colon = text.FindFirstOf(':'); + size_t colon_offset = colon - 13; + + String type = String(text.SubStr(13, colon_offset)).Trim(); + String options = String(text.SubStr(colon + 1)).Trim(); + + bag->Set("type", type); + bag->Set("options", options); + + std::vector<String> tokens = options.Split(";"); + + /* set default values */ + bag->Set("class", LogEntryClassInfo); + bag->Set("log_type", 0); + bag->Set("state", 0); + bag->Set("attempt", 0); + bag->Set("message", text); /* used as 'message' in log table, and 'log_output' in statehist table */ + + if (type.Contains("INITIAL HOST STATE") || + type.Contains("CURRENT HOST STATE") || + type.Contains("HOST ALERT")) { + if (tokens.size() < 5) + return bag; + + bag->Set("host_name", tokens[0]); + bag->Set("state", Host::StateFromString(tokens[1])); + bag->Set("state_type", tokens[2]); + bag->Set("attempt", atoi(tokens[3].CStr())); + bag->Set("plugin_output", tokens[4]); + + if (type.Contains("INITIAL HOST STATE")) { + bag->Set("class", LogEntryClassState); + bag->Set("log_type", LogEntryTypeHostInitialState); + } + else if (type.Contains("CURRENT HOST STATE")) { + bag->Set("class", LogEntryClassState); + bag->Set("log_type", LogEntryTypeHostCurrentState); + } + else { + bag->Set("class", LogEntryClassAlert); + bag->Set("log_type", LogEntryTypeHostAlert); + } + + return bag; + } else if (type.Contains("HOST DOWNTIME ALERT") || type.Contains("HOST FLAPPING ALERT")) { + if (tokens.size() < 3) + return bag; + + bag->Set("host_name", tokens[0]); + bag->Set("state_type", tokens[1]); + bag->Set("comment", tokens[2]); + + if (type.Contains("HOST FLAPPING ALERT")) { + bag->Set("class", LogEntryClassAlert); + bag->Set("log_type", LogEntryTypeHostFlapping); + } else { + bag->Set("class", LogEntryClassAlert); + bag->Set("log_type", LogEntryTypeHostDowntimeAlert); + } + + return bag; + } else if (type.Contains("INITIAL SERVICE STATE") || + type.Contains("CURRENT SERVICE STATE") || + type.Contains("SERVICE ALERT")) { + if (tokens.size() < 6) + return bag; + + bag->Set("host_name", tokens[0]); + bag->Set("service_description", tokens[1]); + bag->Set("state", Service::StateFromString(tokens[2])); + bag->Set("state_type", tokens[3]); + bag->Set("attempt", atoi(tokens[4].CStr())); + bag->Set("plugin_output", tokens[5]); + + if (type.Contains("INITIAL SERVICE STATE")) { + bag->Set("class", LogEntryClassState); + bag->Set("log_type", LogEntryTypeServiceInitialState); + } + else if (type.Contains("CURRENT SERVICE STATE")) { + bag->Set("class", LogEntryClassState); + bag->Set("log_type", LogEntryTypeServiceCurrentState); + } + else { + bag->Set("class", LogEntryClassAlert); + bag->Set("log_type", LogEntryTypeServiceAlert); + } + + return bag; + } else if (type.Contains("SERVICE DOWNTIME ALERT") || + type.Contains("SERVICE FLAPPING ALERT")) { + if (tokens.size() < 4) + return bag; + + bag->Set("host_name", tokens[0]); + bag->Set("service_description", tokens[1]); + bag->Set("state_type", tokens[2]); + bag->Set("comment", tokens[3]); + + if (type.Contains("SERVICE FLAPPING ALERT")) { + bag->Set("class", LogEntryClassAlert); + bag->Set("log_type", LogEntryTypeServiceFlapping); + } else { + bag->Set("class", LogEntryClassAlert); + bag->Set("log_type", LogEntryTypeServiceDowntimeAlert); + } + + return bag; + } else if (type.Contains("TIMEPERIOD TRANSITION")) { + if (tokens.size() < 4) + return bag; + + bag->Set("class", LogEntryClassState); + bag->Set("log_type", LogEntryTypeTimeperiodTransition); + + bag->Set("host_name", tokens[0]); + bag->Set("service_description", tokens[1]); + bag->Set("state_type", tokens[2]); + bag->Set("comment", tokens[3]); + } else if (type.Contains("HOST NOTIFICATION")) { + if (tokens.size() < 6) + return bag; + + bag->Set("contact_name", tokens[0]); + bag->Set("host_name", tokens[1]); + bag->Set("state_type", tokens[2].CStr()); + bag->Set("state", Service::StateFromString(tokens[3])); + bag->Set("command_name", tokens[4]); + bag->Set("plugin_output", tokens[5]); + + bag->Set("class", LogEntryClassNotification); + bag->Set("log_type", LogEntryTypeHostNotification); + + return bag; + } else if (type.Contains("SERVICE NOTIFICATION")) { + if (tokens.size() < 7) + return bag; + + bag->Set("contact_name", tokens[0]); + bag->Set("host_name", tokens[1]); + bag->Set("service_description", tokens[2]); + bag->Set("state_type", tokens[3].CStr()); + bag->Set("state", Service::StateFromString(tokens[4])); + bag->Set("command_name", tokens[5]); + bag->Set("plugin_output", tokens[6]); + + bag->Set("class", LogEntryClassNotification); + bag->Set("log_type", LogEntryTypeServiceNotification); + + return bag; + } else if (type.Contains("PASSIVE HOST CHECK")) { + if (tokens.size() < 3) + return bag; + + bag->Set("host_name", tokens[0]); + bag->Set("state", Host::StateFromString(tokens[1])); + bag->Set("plugin_output", tokens[2]); + + bag->Set("class", LogEntryClassPassive); + + return bag; + } else if (type.Contains("PASSIVE SERVICE CHECK")) { + if (tokens.size() < 4) + return bag; + + bag->Set("host_name", tokens[0]); + bag->Set("service_description", tokens[1]); + bag->Set("state", Host::StateFromString(tokens[2])); + bag->Set("plugin_output", tokens[3]); + + bag->Set("class", LogEntryClassPassive); + + return bag; + } else if (type.Contains("EXTERNAL COMMAND")) { + bag->Set("class", LogEntryClassCommand); + /* string processing not implemented in 1.x */ + + return bag; + } else if (type.Contains("LOG VERSION")) { + bag->Set("class", LogEntryClassProgram); + bag->Set("log_type", LogEntryTypeVersion); + + return bag; + } else if (type.Contains("logging initial states")) { + bag->Set("class", LogEntryClassProgram); + bag->Set("log_type", LogEntryTypeInitialStates); + + return bag; + } else if (type.Contains("starting... (PID=")) { + bag->Set("class", LogEntryClassProgram); + bag->Set("log_type", LogEntryTypeProgramStarting); + + return bag; + } + /* program */ + else if (type.Contains("restarting...") || + type.Contains("shutting down...") || + type.Contains("Bailing out") || + type.Contains("active mode...") || + type.Contains("standby mode...")) { + bag->Set("class", LogEntryClassProgram); + + return bag; + } + + return bag; +} diff --git a/lib/livestatus/livestatuslogutility.hpp b/lib/livestatus/livestatuslogutility.hpp new file mode 100644 index 0000000..66d1154 --- /dev/null +++ b/lib/livestatus/livestatuslogutility.hpp @@ -0,0 +1,60 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef LIVESTATUSLOGUTILITY_H +#define LIVESTATUSLOGUTILITY_H + +#include "livestatus/historytable.hpp" + +using namespace icinga; + +namespace icinga +{ + +enum LogEntryType { + LogEntryTypeHostAlert, + LogEntryTypeHostDowntimeAlert, + LogEntryTypeHostFlapping, + LogEntryTypeHostNotification, + LogEntryTypeHostInitialState, + LogEntryTypeHostCurrentState, + LogEntryTypeServiceAlert, + LogEntryTypeServiceDowntimeAlert, + LogEntryTypeServiceFlapping, + LogEntryTypeServiceNotification, + LogEntryTypeServiceInitialState, + LogEntryTypeServiceCurrentState, + LogEntryTypeTimeperiodTransition, + LogEntryTypeVersion, + LogEntryTypeInitialStates, + LogEntryTypeProgramStarting +}; + +enum LogEntryClass { + LogEntryClassInfo = 0, + LogEntryClassAlert = 1, + LogEntryClassProgram = 2, + LogEntryClassNotification = 3, + LogEntryClassPassive = 4, + LogEntryClassCommand = 5, + LogEntryClassState = 6, + LogEntryClassText = 7 +}; + +/** + * @ingroup livestatus + */ +class LivestatusLogUtility +{ +public: + static void CreateLogIndex(const String& path, std::map<time_t, String>& index); + static void CreateLogIndexFileHandler(const String& path, std::map<time_t, String>& index); + static void CreateLogCache(std::map<time_t, String> index, HistoryTable *table, time_t from, time_t until, const AddRowFunction& addRowFn); + static Dictionary::Ptr GetAttributes(const String& text); + +private: + LivestatusLogUtility(); +}; + +} + +#endif /* LIVESTATUSLOGUTILITY_H */ diff --git a/lib/livestatus/livestatusquery.cpp b/lib/livestatus/livestatusquery.cpp new file mode 100644 index 0000000..0f9b3da --- /dev/null +++ b/lib/livestatus/livestatusquery.cpp @@ -0,0 +1,648 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/livestatusquery.hpp" +#include "livestatus/countaggregator.hpp" +#include "livestatus/sumaggregator.hpp" +#include "livestatus/minaggregator.hpp" +#include "livestatus/maxaggregator.hpp" +#include "livestatus/avgaggregator.hpp" +#include "livestatus/stdaggregator.hpp" +#include "livestatus/invsumaggregator.hpp" +#include "livestatus/invavgaggregator.hpp" +#include "livestatus/attributefilter.hpp" +#include "livestatus/negatefilter.hpp" +#include "livestatus/orfilter.hpp" +#include "livestatus/andfilter.hpp" +#include "icinga/externalcommandprocessor.hpp" +#include "base/debug.hpp" +#include "base/convert.hpp" +#include "base/objectlock.hpp" +#include "base/logger.hpp" +#include "base/exception.hpp" +#include "base/utility.hpp" +#include "base/json.hpp" +#include "base/serializer.hpp" +#include "base/timer.hpp" +#include "base/initialize.hpp" +#include <boost/algorithm/string/replace.hpp> +#include <boost/algorithm/string/join.hpp> + +using namespace icinga; + +static int l_ExternalCommands = 0; +static std::mutex l_QueryMutex; + +LivestatusQuery::LivestatusQuery(const std::vector<String>& lines, const String& compat_log_path) + : m_KeepAlive(false), m_OutputFormat("csv"), m_ColumnHeaders(true), m_Limit(-1), m_ErrorCode(0), + m_LogTimeFrom(0), m_LogTimeUntil(static_cast<long>(Utility::GetTime())) +{ + if (lines.size() == 0) { + m_Verb = "ERROR"; + m_ErrorCode = LivestatusErrorQuery; + m_ErrorMessage = "Empty Query. Aborting."; + return; + } + + String msg; + for (const String& line : lines) { + msg += line + "\n"; + } + Log(LogDebug, "LivestatusQuery", msg); + + m_CompatLogPath = compat_log_path; + + /* default separators */ + m_Separators.emplace_back("\n"); + m_Separators.emplace_back(";"); + m_Separators.emplace_back(","); + m_Separators.emplace_back("|"); + + String line = lines[0]; + + size_t sp_index = line.FindFirstOf(" "); + + if (sp_index == String::NPos) + BOOST_THROW_EXCEPTION(std::runtime_error("Livestatus header must contain a verb.")); + + String verb = line.SubStr(0, sp_index); + String target = line.SubStr(sp_index + 1); + + m_Verb = verb; + + if (m_Verb == "COMMAND") { + m_KeepAlive = true; + m_Command = target; + } else if (m_Verb == "GET") { + m_Table = target; + } else { + m_Verb = "ERROR"; + m_ErrorCode = LivestatusErrorQuery; + m_ErrorMessage = "Unknown livestatus verb: " + m_Verb; + return; + } + + std::deque<Filter::Ptr> filters, stats; + std::deque<Aggregator::Ptr> aggregators; + + for (unsigned int i = 1; i < lines.size(); i++) { + line = lines[i]; + + size_t col_index = line.FindFirstOf(":"); + String header = line.SubStr(0, col_index); + String params; + + //OutputFormat:json or OutputFormat: json + if (line.GetLength() > col_index + 1) + params = line.SubStr(col_index + 1).Trim(); + + if (header == "ResponseHeader") + m_ResponseHeader = params; + else if (header == "OutputFormat") + m_OutputFormat = params; + else if (header == "KeepAlive") + m_KeepAlive = (params == "on"); + else if (header == "Columns") { + m_ColumnHeaders = false; // Might be explicitly re-enabled later on + m_Columns = params.Split(" "); + } else if (header == "Separators") { + std::vector<String> separators = params.Split(" "); + + /* ugly ascii long to char conversion, but works */ + if (separators.size() > 0) + m_Separators[0] = String(1, static_cast<char>(Convert::ToLong(separators[0]))); + if (separators.size() > 1) + m_Separators[1] = String(1, static_cast<char>(Convert::ToLong(separators[1]))); + if (separators.size() > 2) + m_Separators[2] = String(1, static_cast<char>(Convert::ToLong(separators[2]))); + if (separators.size() > 3) + m_Separators[3] = String(1, static_cast<char>(Convert::ToLong(separators[3]))); + } else if (header == "ColumnHeaders") + m_ColumnHeaders = (params == "on"); + else if (header == "Limit") + m_Limit = Convert::ToLong(params); + else if (header == "Filter") { + Filter::Ptr filter = ParseFilter(params, m_LogTimeFrom, m_LogTimeUntil); + + if (!filter) { + m_Verb = "ERROR"; + m_ErrorCode = LivestatusErrorQuery; + m_ErrorMessage = "Invalid filter specification: " + line; + return; + } + + filters.push_back(filter); + } else if (header == "Stats") { + m_ColumnHeaders = false; // Might be explicitly re-enabled later on + + std::vector<String> tokens = params.Split(" "); + + if (tokens.size() < 2) { + m_Verb = "ERROR"; + m_ErrorCode = LivestatusErrorQuery; + m_ErrorMessage = "Missing aggregator column name: " + line; + return; + } + + String aggregate_arg = tokens[0]; + String aggregate_attr = tokens[1]; + + Aggregator::Ptr aggregator; + Filter::Ptr filter; + + if (aggregate_arg == "sum") { + aggregator = new SumAggregator(aggregate_attr); + } else if (aggregate_arg == "min") { + aggregator = new MinAggregator(aggregate_attr); + } else if (aggregate_arg == "max") { + aggregator = new MaxAggregator(aggregate_attr); + } else if (aggregate_arg == "avg") { + aggregator = new AvgAggregator(aggregate_attr); + } else if (aggregate_arg == "std") { + aggregator = new StdAggregator(aggregate_attr); + } else if (aggregate_arg == "suminv") { + aggregator = new InvSumAggregator(aggregate_attr); + } else if (aggregate_arg == "avginv") { + aggregator = new InvAvgAggregator(aggregate_attr); + } else { + filter = ParseFilter(params, m_LogTimeFrom, m_LogTimeUntil); + + if (!filter) { + m_Verb = "ERROR"; + m_ErrorCode = LivestatusErrorQuery; + m_ErrorMessage = "Invalid filter specification: " + line; + return; + } + + aggregator = new CountAggregator(); + } + + aggregator->SetFilter(filter); + aggregators.push_back(aggregator); + + stats.push_back(filter); + } else if (header == "Or" || header == "And" || header == "StatsOr" || header == "StatsAnd") { + std::deque<Filter::Ptr>& deq = (header == "Or" || header == "And") ? filters : stats; + + unsigned int num = Convert::ToLong(params); + CombinerFilter::Ptr filter; + + if (header == "Or" || header == "StatsOr") { + filter = new OrFilter(); + Log(LogDebug, "LivestatusQuery") + << "Add OR filter for " << params << " column(s). " << deq.size() << " filters available."; + } else { + filter = new AndFilter(); + Log(LogDebug, "LivestatusQuery") + << "Add AND filter for " << params << " column(s). " << deq.size() << " filters available."; + } + + if (num > deq.size()) { + m_Verb = "ERROR"; + m_ErrorCode = 451; + m_ErrorMessage = "Or/StatsOr is referencing " + Convert::ToString(num) + " filters; stack only contains " + Convert::ToString(static_cast<long>(deq.size())) + " filters"; + return; + } + + while (num > 0 && num--) { + filter->AddSubFilter(deq.back()); + Log(LogDebug, "LivestatusQuery") + << "Add " << num << " filter."; + deq.pop_back(); + if (&deq == &stats) + aggregators.pop_back(); + } + + deq.emplace_back(filter); + if (&deq == &stats) { + Aggregator::Ptr aggregator = new CountAggregator(); + aggregator->SetFilter(filter); + aggregators.push_back(aggregator); + } + } else if (header == "Negate" || header == "StatsNegate") { + std::deque<Filter::Ptr>& deq = (header == "Negate") ? filters : stats; + + if (deq.empty()) { + m_Verb = "ERROR"; + m_ErrorCode = 451; + m_ErrorMessage = "Negate/StatsNegate used, however the filter stack is empty"; + return; + } + + Filter::Ptr filter = deq.back(); + deq.pop_back(); + + if (!filter) { + m_Verb = "ERROR"; + m_ErrorCode = 451; + m_ErrorMessage = "Negate/StatsNegate used, however last stats doesn't have a filter"; + return; + } + + deq.push_back(new NegateFilter(filter)); + + if (deq == stats) { + Aggregator::Ptr aggregator = aggregators.back(); + aggregator->SetFilter(filter); + } + } + } + + /* Combine all top-level filters into a single filter. */ + AndFilter::Ptr top_filter = new AndFilter(); + + for (const Filter::Ptr& filter : filters) { + top_filter->AddSubFilter(filter); + } + + m_Filter = top_filter; + m_Aggregators.swap(aggregators); +} + +int LivestatusQuery::GetExternalCommands() +{ + std::unique_lock<std::mutex> lock(l_QueryMutex); + + return l_ExternalCommands; +} + +Filter::Ptr LivestatusQuery::ParseFilter(const String& params, unsigned long& from, unsigned long& until) +{ + /* + * time >= 1382696656 + * type = SERVICE FLAPPING ALERT + */ + std::vector<String> tokens; + size_t sp_index; + String temp_buffer = params; + + /* extract attr and op */ + for (int i = 0; i < 2; i++) { + sp_index = temp_buffer.FindFirstOf(" "); + + /* check if this is the last argument */ + if (sp_index == String::NPos) { + /* 'attr op' or 'attr op val' is valid */ + if (i < 1) + BOOST_THROW_EXCEPTION(std::runtime_error("Livestatus filter '" + params + "' does not contain all required fields.")); + + break; + } + + tokens.emplace_back(temp_buffer.SubStr(0, sp_index)); + temp_buffer = temp_buffer.SubStr(sp_index + 1); + } + + /* add the rest as value */ + tokens.emplace_back(std::move(temp_buffer)); + + if (tokens.size() == 2) + tokens.emplace_back(""); + + if (tokens.size() < 3) + return nullptr; + + bool negate = false; + String attr = tokens[0]; + String op = tokens[1]; + String val = tokens[2]; + + if (op == "!=") { + op = "="; + negate = true; + } else if (op == "!~") { + op = "~"; + negate = true; + } else if (op == "!=~") { + op = "=~"; + negate = true; + } else if (op == "!~~") { + op = "~~"; + negate = true; + } + + Filter::Ptr filter = new AttributeFilter(attr, op, val); + + if (negate) + filter = new NegateFilter(filter); + + /* pre-filter log time duration */ + if (attr == "time") { + if (op == "<" || op == "<=") { + until = Convert::ToLong(val); + } else if (op == ">" || op == ">=") { + from = Convert::ToLong(val); + } + } + + Log(LogDebug, "LivestatusQuery") + << "Parsed filter with attr: '" << attr << "' op: '" << op << "' val: '" << val << "'."; + + return filter; +} + +void LivestatusQuery::BeginResultSet(std::ostream& fp) const +{ + if (m_OutputFormat == "json" || m_OutputFormat == "python") + fp << "["; +} + +void LivestatusQuery::EndResultSet(std::ostream& fp) const +{ + if (m_OutputFormat == "json" || m_OutputFormat == "python") + fp << "]"; +} + +void LivestatusQuery::AppendResultRow(std::ostream& fp, const Array::Ptr& row, bool& first_row) const +{ + if (m_OutputFormat == "csv") { + bool first = true; + + ObjectLock rlock(row); + for (const Value& value : row) { + if (first) + first = false; + else + fp << m_Separators[1]; + + if (value.IsObjectType<Array>()) + PrintCsvArray(fp, value, 0); + else + fp << value; + } + + fp << m_Separators[0]; + } else if (m_OutputFormat == "json") { + if (!first_row) + fp << ", "; + + fp << JsonEncode(row); + } else if (m_OutputFormat == "python") { + if (!first_row) + fp << ", "; + + PrintPythonArray(fp, row); + } + + first_row = false; +} + +void LivestatusQuery::PrintCsvArray(std::ostream& fp, const Array::Ptr& array, int level) const +{ + bool first = true; + + ObjectLock olock(array); + for (const Value& value : array) { + if (first) + first = false; + else + fp << ((level == 0) ? m_Separators[2] : m_Separators[3]); + + if (value.IsObjectType<Array>()) + PrintCsvArray(fp, value, level + 1); + else if (value.IsBoolean()) + fp << Convert::ToLong(value); + else + fp << value; + } +} + +void LivestatusQuery::PrintPythonArray(std::ostream& fp, const Array::Ptr& rs) const +{ + fp << "[ "; + + bool first = true; + + for (const Value& value : rs) { + if (first) + first = false; + else + fp << ", "; + + if (value.IsObjectType<Array>()) + PrintPythonArray(fp, value); + else if (value.IsNumber()) + fp << value; + else + fp << QuoteStringPython(value); + } + + fp << " ]"; +} + +String LivestatusQuery::QuoteStringPython(const String& str) { + String result = str; + boost::algorithm::replace_all(result, "\"", "\\\""); + return "r\"" + result + "\""; +} + +void LivestatusQuery::ExecuteGetHelper(const Stream::Ptr& stream) +{ + Log(LogNotice, "LivestatusQuery") + << "Table: " << m_Table; + + Table::Ptr table = Table::GetByName(m_Table, m_CompatLogPath, m_LogTimeFrom, m_LogTimeUntil); + + if (!table) { + SendResponse(stream, LivestatusErrorNotFound, "Table '" + m_Table + "' does not exist."); + + return; + } + + std::vector<LivestatusRowValue> objects = table->FilterRows(m_Filter, m_Limit); + std::vector<String> columns; + + if (m_Columns.size() > 0) + columns = m_Columns; + else + columns = table->GetColumnNames(); + + std::ostringstream result; + bool first_row = true; + BeginResultSet(result); + + if (m_Aggregators.empty()) { + typedef std::pair<String, Column> ColumnPair; + + std::vector<ColumnPair> column_objs; + column_objs.reserve(columns.size()); + + for (const String& columnName : columns) + column_objs.emplace_back(columnName, table->GetColumn(columnName)); + + ArrayData header; + + for (const LivestatusRowValue& object : objects) { + ArrayData row; + + row.reserve(column_objs.size()); + + for (const ColumnPair& cv : column_objs) { + if (m_ColumnHeaders) + header.push_back(cv.first); + + row.push_back(cv.second.ExtractValue(object.Row, object.GroupByType, object.GroupByObject)); + } + + if (m_ColumnHeaders) { + AppendResultRow(result, new Array(std::move(header)), first_row); + m_ColumnHeaders = false; + } + + AppendResultRow(result, new Array(std::move(row)), first_row); + } + } else { + std::map<std::vector<Value>, std::vector<AggregatorState *> > allStats; + + /* add aggregated stats */ + for (const LivestatusRowValue& object : objects) { + std::vector<Value> statsKey; + + for (const String& columnName : m_Columns) { + Column column = table->GetColumn(columnName); + statsKey.emplace_back(column.ExtractValue(object.Row, object.GroupByType, object.GroupByObject)); + } + + auto it = allStats.find(statsKey); + + if (it == allStats.end()) { + std::vector<AggregatorState *> newStats(m_Aggregators.size(), nullptr); + it = allStats.insert(std::make_pair(statsKey, newStats)).first; + } + + auto& stats = it->second; + + int index = 0; + + for (const Aggregator::Ptr& aggregator : m_Aggregators) { + aggregator->Apply(table, object.Row, &stats[index]); + index++; + } + } + + /* add column headers both for raw and aggregated data */ + if (m_ColumnHeaders) { + ArrayData header; + + for (const String& columnName : m_Columns) { + header.push_back(columnName); + } + + for (size_t i = 1; i <= m_Aggregators.size(); i++) { + header.push_back("stats_" + Convert::ToString(i)); + } + + AppendResultRow(result, new Array(std::move(header)), first_row); + } + + for (const auto& kv : allStats) { + ArrayData row; + + row.reserve(m_Columns.size() + m_Aggregators.size()); + + for (const Value& keyPart : kv.first) { + row.push_back(keyPart); + } + + auto& stats = kv.second; + + for (size_t i = 0; i < m_Aggregators.size(); i++) + row.push_back(m_Aggregators[i]->GetResultAndFreeState(stats[i])); + + AppendResultRow(result, new Array(std::move(row)), first_row); + } + + /* add a bogus zero value if aggregated is empty*/ + if (allStats.empty()) { + ArrayData row; + + row.reserve(m_Aggregators.size()); + + for (size_t i = 1; i <= m_Aggregators.size(); i++) { + row.push_back(0); + } + + AppendResultRow(result, new Array(std::move(row)), first_row); + } + } + + EndResultSet(result); + + SendResponse(stream, LivestatusErrorOK, result.str()); +} + +void LivestatusQuery::ExecuteCommandHelper(const Stream::Ptr& stream) +{ + { + std::unique_lock<std::mutex> lock(l_QueryMutex); + + l_ExternalCommands++; + } + + Log(LogNotice, "LivestatusQuery") + << "Executing command: " << m_Command; + ExternalCommandProcessor::Execute(m_Command); + SendResponse(stream, LivestatusErrorOK, ""); +} + +void LivestatusQuery::ExecuteErrorHelper(const Stream::Ptr& stream) +{ + Log(LogDebug, "LivestatusQuery") + << "ERROR: Code: '" << m_ErrorCode << "' Message: '" << m_ErrorMessage << "'."; + SendResponse(stream, m_ErrorCode, m_ErrorMessage); +} + +void LivestatusQuery::SendResponse(const Stream::Ptr& stream, int code, const String& data) +{ + if (m_ResponseHeader == "fixed16") + PrintFixed16(stream, code, data); + + if (m_ResponseHeader == "fixed16" || code == LivestatusErrorOK) { + try { + stream->Write(data.CStr(), data.GetLength()); + } catch (const std::exception&) { + Log(LogCritical, "LivestatusQuery", "Cannot write query response to socket."); + } + } +} + +void LivestatusQuery::PrintFixed16(const Stream::Ptr& stream, int code, const String& data) +{ + ASSERT(code >= 100 && code <= 999); + + String sCode = Convert::ToString(code); + String sLength = Convert::ToString(static_cast<long>(data.GetLength())); + + String header = sCode + String(16 - 3 - sLength.GetLength() - 1, ' ') + sLength + m_Separators[0]; + + try { + stream->Write(header.CStr(), header.GetLength()); + } catch (const std::exception&) { + Log(LogCritical, "LivestatusQuery", "Cannot write to TCP socket."); + } +} + +bool LivestatusQuery::Execute(const Stream::Ptr& stream) +{ + try { + Log(LogNotice, "LivestatusQuery") + << "Executing livestatus query: " << m_Verb; + + if (m_Verb == "GET") + ExecuteGetHelper(stream); + else if (m_Verb == "COMMAND") + ExecuteCommandHelper(stream); + else if (m_Verb == "ERROR") + ExecuteErrorHelper(stream); + else + BOOST_THROW_EXCEPTION(std::runtime_error("Invalid livestatus query verb.")); + } catch (const std::exception& ex) { + SendResponse(stream, LivestatusErrorQuery, DiagnosticInformation(ex)); + } + + if (!m_KeepAlive) { + stream->Close(); + return false; + } + + return true; +} diff --git a/lib/livestatus/livestatusquery.hpp b/lib/livestatus/livestatusquery.hpp new file mode 100644 index 0000000..910cc16 --- /dev/null +++ b/lib/livestatus/livestatusquery.hpp @@ -0,0 +1,90 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef LIVESTATUSQUERY_H +#define LIVESTATUSQUERY_H + +#include "livestatus/filter.hpp" +#include "livestatus/aggregator.hpp" +#include "base/object.hpp" +#include "base/array.hpp" +#include "base/stream.hpp" +#include "base/scriptframe.hpp" +#include <deque> + +using namespace icinga; + +namespace icinga +{ + +enum LivestatusError +{ + LivestatusErrorOK = 200, + LivestatusErrorNotFound = 404, + LivestatusErrorQuery = 452 +}; + +/** + * @ingroup livestatus + */ +class LivestatusQuery final : public Object +{ +public: + DECLARE_PTR_TYPEDEFS(LivestatusQuery); + + LivestatusQuery(const std::vector<String>& lines, const String& compat_log_path); + + bool Execute(const Stream::Ptr& stream); + + static int GetExternalCommands(); + +private: + String m_Verb; + + bool m_KeepAlive; + + /* Parameters for GET queries. */ + String m_Table; + std::vector<String> m_Columns; + std::vector<String> m_Separators; + + Filter::Ptr m_Filter; + std::deque<Aggregator::Ptr> m_Aggregators; + + String m_OutputFormat; + bool m_ColumnHeaders; + int m_Limit; + + String m_ResponseHeader; + + /* Parameters for COMMAND/SCRIPT queries. */ + String m_Command; + String m_Session; + + /* Parameters for invalid queries. */ + int m_ErrorCode; + String m_ErrorMessage; + + unsigned long m_LogTimeFrom; + unsigned long m_LogTimeUntil; + String m_CompatLogPath; + + void BeginResultSet(std::ostream& fp) const; + void EndResultSet(std::ostream& fp) const; + void AppendResultRow(std::ostream& fp, const Array::Ptr& row, bool& first_row) const; + void PrintCsvArray(std::ostream& fp, const Array::Ptr& array, int level) const; + void PrintPythonArray(std::ostream& fp, const Array::Ptr& array) const; + static String QuoteStringPython(const String& str); + + void ExecuteGetHelper(const Stream::Ptr& stream); + void ExecuteCommandHelper(const Stream::Ptr& stream); + void ExecuteErrorHelper(const Stream::Ptr& stream); + + void SendResponse(const Stream::Ptr& stream, int code, const String& data); + void PrintFixed16(const Stream::Ptr& stream, int code, const String& data); + + static Filter::Ptr ParseFilter(const String& params, unsigned long& from, unsigned long& until); +}; + +} + +#endif /* LIVESTATUSQUERY_H */ diff --git a/lib/livestatus/logtable.cpp b/lib/livestatus/logtable.cpp new file mode 100644 index 0000000..c1358dd --- /dev/null +++ b/lib/livestatus/logtable.cpp @@ -0,0 +1,229 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/logtable.hpp" +#include "livestatus/livestatuslogutility.hpp" +#include "livestatus/hoststable.hpp" +#include "livestatus/servicestable.hpp" +#include "livestatus/contactstable.hpp" +#include "livestatus/commandstable.hpp" +#include "icinga/icingaapplication.hpp" +#include "icinga/cib.hpp" +#include "icinga/service.hpp" +#include "icinga/host.hpp" +#include "icinga/user.hpp" +#include "icinga/checkcommand.hpp" +#include "icinga/eventcommand.hpp" +#include "icinga/notificationcommand.hpp" +#include "base/convert.hpp" +#include "base/utility.hpp" +#include "base/logger.hpp" +#include "base/application.hpp" +#include "base/objectlock.hpp" +#include <boost/algorithm/string.hpp> +#include <boost/algorithm/string/replace.hpp> +#include <boost/algorithm/string/predicate.hpp> +#include <fstream> + +using namespace icinga; + +LogTable::LogTable(const String& compat_log_path, time_t from, time_t until) +{ + /* store attributes for FetchRows */ + m_TimeFrom = from; + m_TimeUntil = until; + m_CompatLogPath = compat_log_path; + + AddColumns(this); +} + +void LogTable::AddColumns(Table *table, const String& prefix, + const Column::ObjectAccessor& objectAccessor) +{ + table->AddColumn(prefix + "time", Column(&LogTable::TimeAccessor, objectAccessor)); + table->AddColumn(prefix + "lineno", Column(&LogTable::LinenoAccessor, objectAccessor)); + table->AddColumn(prefix + "class", Column(&LogTable::ClassAccessor, objectAccessor)); + table->AddColumn(prefix + "message", Column(&LogTable::MessageAccessor, objectAccessor)); + table->AddColumn(prefix + "type", Column(&LogTable::TypeAccessor, objectAccessor)); + table->AddColumn(prefix + "options", Column(&LogTable::OptionsAccessor, objectAccessor)); + table->AddColumn(prefix + "comment", Column(&LogTable::CommentAccessor, objectAccessor)); + table->AddColumn(prefix + "plugin_output", Column(&LogTable::PluginOutputAccessor, objectAccessor)); + table->AddColumn(prefix + "state", Column(&LogTable::StateAccessor, objectAccessor)); + table->AddColumn(prefix + "state_type", Column(&LogTable::StateTypeAccessor, objectAccessor)); + table->AddColumn(prefix + "attempt", Column(&LogTable::AttemptAccessor, objectAccessor)); + table->AddColumn(prefix + "service_description", Column(&LogTable::ServiceDescriptionAccessor, objectAccessor)); + table->AddColumn(prefix + "host_name", Column(&LogTable::HostNameAccessor, objectAccessor)); + table->AddColumn(prefix + "contact_name", Column(&LogTable::ContactNameAccessor, objectAccessor)); + table->AddColumn(prefix + "command_name", Column(&LogTable::CommandNameAccessor, objectAccessor)); + + HostsTable::AddColumns(table, "current_host_", [objectAccessor](const Value& row, LivestatusGroupByType, const Object::Ptr&) -> Value { + return HostAccessor(row, objectAccessor); + }); + ServicesTable::AddColumns(table, "current_service_", [objectAccessor](const Value& row, LivestatusGroupByType, const Object::Ptr&) -> Value { + return ServiceAccessor(row, objectAccessor); + }); + ContactsTable::AddColumns(table, "current_contact_", [objectAccessor](const Value& row, LivestatusGroupByType, const Object::Ptr&) -> Value { + return ContactAccessor(row, objectAccessor); + }); + CommandsTable::AddColumns(table, "current_command_", [objectAccessor](const Value& row, LivestatusGroupByType, const Object::Ptr&) -> Value { + return CommandAccessor(row, objectAccessor); + }); +} + +String LogTable::GetName() const +{ + return "log"; +} + +String LogTable::GetPrefix() const +{ + return "log"; +} + +void LogTable::FetchRows(const AddRowFunction& addRowFn) +{ + Log(LogDebug, "LogTable") + << "Pre-selecting log file from " << m_TimeFrom << " until " << m_TimeUntil; + + /* create log file index */ + LivestatusLogUtility::CreateLogIndex(m_CompatLogPath, m_LogFileIndex); + + /* generate log cache */ + LivestatusLogUtility::CreateLogCache(m_LogFileIndex, this, m_TimeFrom, m_TimeUntil, addRowFn); +} + +/* gets called in LivestatusLogUtility::CreateLogCache */ +void LogTable::UpdateLogEntries(const Dictionary::Ptr& log_entry_attrs, int line_count, int lineno, const AddRowFunction& addRowFn) +{ + /* additional attributes only for log table */ + log_entry_attrs->Set("lineno", lineno); + + addRowFn(log_entry_attrs, LivestatusGroupByNone, Empty); +} + +Object::Ptr LogTable::HostAccessor(const Value& row, const Column::ObjectAccessor&) +{ + String host_name = static_cast<Dictionary::Ptr>(row)->Get("host_name"); + + if (host_name.IsEmpty()) + return nullptr; + + return Host::GetByName(host_name); +} + +Object::Ptr LogTable::ServiceAccessor(const Value& row, const Column::ObjectAccessor&) +{ + String host_name = static_cast<Dictionary::Ptr>(row)->Get("host_name"); + String service_description = static_cast<Dictionary::Ptr>(row)->Get("service_description"); + + if (service_description.IsEmpty() || host_name.IsEmpty()) + return nullptr; + + return Service::GetByNamePair(host_name, service_description); +} + +Object::Ptr LogTable::ContactAccessor(const Value& row, const Column::ObjectAccessor&) +{ + String contact_name = static_cast<Dictionary::Ptr>(row)->Get("contact_name"); + + if (contact_name.IsEmpty()) + return nullptr; + + return User::GetByName(contact_name); +} + +Object::Ptr LogTable::CommandAccessor(const Value& row, const Column::ObjectAccessor&) +{ + String command_name = static_cast<Dictionary::Ptr>(row)->Get("command_name"); + + if (command_name.IsEmpty()) + return nullptr; + + CheckCommand::Ptr check_command = CheckCommand::GetByName(command_name); + if (!check_command) { + EventCommand::Ptr event_command = EventCommand::GetByName(command_name); + if (!event_command) { + NotificationCommand::Ptr notification_command = NotificationCommand::GetByName(command_name); + if (!notification_command) + return nullptr; + else + return notification_command; + } else + return event_command; + } else + return check_command; +} + +Value LogTable::TimeAccessor(const Value& row) +{ + return static_cast<Dictionary::Ptr>(row)->Get("time"); +} + +Value LogTable::LinenoAccessor(const Value& row) +{ + return static_cast<Dictionary::Ptr>(row)->Get("lineno"); +} + +Value LogTable::ClassAccessor(const Value& row) +{ + return static_cast<Dictionary::Ptr>(row)->Get("class"); +} + +Value LogTable::MessageAccessor(const Value& row) +{ + return static_cast<Dictionary::Ptr>(row)->Get("message"); +} + +Value LogTable::TypeAccessor(const Value& row) +{ + return static_cast<Dictionary::Ptr>(row)->Get("type"); +} + +Value LogTable::OptionsAccessor(const Value& row) +{ + return static_cast<Dictionary::Ptr>(row)->Get("options"); +} + +Value LogTable::CommentAccessor(const Value& row) +{ + return static_cast<Dictionary::Ptr>(row)->Get("comment"); +} + +Value LogTable::PluginOutputAccessor(const Value& row) +{ + return static_cast<Dictionary::Ptr>(row)->Get("plugin_output"); +} + +Value LogTable::StateAccessor(const Value& row) +{ + return static_cast<Dictionary::Ptr>(row)->Get("state"); +} + +Value LogTable::StateTypeAccessor(const Value& row) +{ + return static_cast<Dictionary::Ptr>(row)->Get("state_type"); +} + +Value LogTable::AttemptAccessor(const Value& row) +{ + return static_cast<Dictionary::Ptr>(row)->Get("attempt"); +} + +Value LogTable::ServiceDescriptionAccessor(const Value& row) +{ + return static_cast<Dictionary::Ptr>(row)->Get("service_description"); +} + +Value LogTable::HostNameAccessor(const Value& row) +{ + return static_cast<Dictionary::Ptr>(row)->Get("host_name"); +} + +Value LogTable::ContactNameAccessor(const Value& row) +{ + return static_cast<Dictionary::Ptr>(row)->Get("contact_name"); +} + +Value LogTable::CommandNameAccessor(const Value& row) +{ + return static_cast<Dictionary::Ptr>(row)->Get("command_name"); +} diff --git a/lib/livestatus/logtable.hpp b/lib/livestatus/logtable.hpp new file mode 100644 index 0000000..7a89310 --- /dev/null +++ b/lib/livestatus/logtable.hpp @@ -0,0 +1,65 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef LOGTABLE_H +#define LOGTABLE_H + +#include "livestatus/historytable.hpp" + +using namespace icinga; + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +class LogTable final : public HistoryTable +{ +public: + DECLARE_PTR_TYPEDEFS(LogTable); + + LogTable(const String& compat_log_path, time_t from, time_t until); + + static void AddColumns(Table *table, const String& prefix = String(), + const Column::ObjectAccessor& objectAccessor = Column::ObjectAccessor()); + + String GetName() const override; + String GetPrefix() const override; + + void UpdateLogEntries(const Dictionary::Ptr& log_entry_attrs, int line_count, int lineno, const AddRowFunction& addRowFn) override; + +protected: + void FetchRows(const AddRowFunction& addRowFn) override; + + static Object::Ptr HostAccessor(const Value& row, const Column::ObjectAccessor& parentObjectAccessor); + static Object::Ptr ServiceAccessor(const Value& row, const Column::ObjectAccessor& parentObjectAccessor); + static Object::Ptr ContactAccessor(const Value& row, const Column::ObjectAccessor& parentObjectAccessor); + static Object::Ptr CommandAccessor(const Value& row, const Column::ObjectAccessor& parentObjectAccessor); + + static Value TimeAccessor(const Value& row); + static Value LinenoAccessor(const Value& row); + static Value ClassAccessor(const Value& row); + static Value MessageAccessor(const Value& row); + static Value TypeAccessor(const Value& row); + static Value OptionsAccessor(const Value& row); + static Value CommentAccessor(const Value& row); + static Value PluginOutputAccessor(const Value& row); + static Value StateAccessor(const Value& row); + static Value StateTypeAccessor(const Value& row); + static Value AttemptAccessor(const Value& row); + static Value ServiceDescriptionAccessor(const Value& row); + static Value HostNameAccessor(const Value& row); + static Value ContactNameAccessor(const Value& row); + static Value CommandNameAccessor(const Value& row); + +private: + std::map<time_t, String> m_LogFileIndex; + std::map<time_t, Dictionary::Ptr> m_RowsCache; + time_t m_TimeFrom; + time_t m_TimeUntil; + String m_CompatLogPath; +}; + +} + +#endif /* LOGTABLE_H */ diff --git a/lib/livestatus/maxaggregator.cpp b/lib/livestatus/maxaggregator.cpp new file mode 100644 index 0000000..375d24b --- /dev/null +++ b/lib/livestatus/maxaggregator.cpp @@ -0,0 +1,38 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/maxaggregator.hpp" + +using namespace icinga; + +MaxAggregator::MaxAggregator(String attr) + : m_MaxAttr(std::move(attr)) +{ } + +MaxAggregatorState *MaxAggregator::EnsureState(AggregatorState **state) +{ + if (!*state) + *state = new MaxAggregatorState(); + + return static_cast<MaxAggregatorState *>(*state); +} + +void MaxAggregator::Apply(const Table::Ptr& table, const Value& row, AggregatorState **state) +{ + Column column = table->GetColumn(m_MaxAttr); + + Value value = column.ExtractValue(row); + + MaxAggregatorState *pstate = EnsureState(state); + + if (value > pstate->Max) + pstate->Max = value; +} + +double MaxAggregator::GetResultAndFreeState(AggregatorState *state) const +{ + MaxAggregatorState *pstate = EnsureState(&state); + double result = pstate->Max; + delete pstate; + + return result; +} diff --git a/lib/livestatus/maxaggregator.hpp b/lib/livestatus/maxaggregator.hpp new file mode 100644 index 0000000..5bff5f9 --- /dev/null +++ b/lib/livestatus/maxaggregator.hpp @@ -0,0 +1,41 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef MAXAGGREGATOR_H +#define MAXAGGREGATOR_H + +#include "livestatus/table.hpp" +#include "livestatus/aggregator.hpp" + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +struct MaxAggregatorState final : public AggregatorState +{ + double Max{0}; +}; + +/** + * @ingroup livestatus + */ +class MaxAggregator final : public Aggregator +{ +public: + DECLARE_PTR_TYPEDEFS(MaxAggregator); + + MaxAggregator(String attr); + + void Apply(const Table::Ptr& table, const Value& row, AggregatorState **state) override; + double GetResultAndFreeState(AggregatorState *state) const override; + +private: + String m_MaxAttr; + + static MaxAggregatorState *EnsureState(AggregatorState **state); +}; + +} + +#endif /* MAXAGGREGATOR_H */ diff --git a/lib/livestatus/minaggregator.cpp b/lib/livestatus/minaggregator.cpp new file mode 100644 index 0000000..06cb76e --- /dev/null +++ b/lib/livestatus/minaggregator.cpp @@ -0,0 +1,45 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/minaggregator.hpp" + +using namespace icinga; + +MinAggregator::MinAggregator(String attr) + : m_MinAttr(std::move(attr)) +{ } + +MinAggregatorState *MinAggregator::EnsureState(AggregatorState **state) +{ + if (!*state) + *state = new MinAggregatorState(); + + return static_cast<MinAggregatorState *>(*state); +} + +void MinAggregator::Apply(const Table::Ptr& table, const Value& row, AggregatorState **state) +{ + Column column = table->GetColumn(m_MinAttr); + + Value value = column.ExtractValue(row); + + MinAggregatorState *pstate = EnsureState(state); + + if (value < pstate->Min) + pstate->Min = value; +} + +double MinAggregator::GetResultAndFreeState(AggregatorState *state) const +{ + MinAggregatorState *pstate = EnsureState(&state); + + double result; + + if (pstate->Min == DBL_MAX) + result = 0; + else + result = pstate->Min; + + delete pstate; + + return result; +} diff --git a/lib/livestatus/minaggregator.hpp b/lib/livestatus/minaggregator.hpp new file mode 100644 index 0000000..71a9d89 --- /dev/null +++ b/lib/livestatus/minaggregator.hpp @@ -0,0 +1,42 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef MINAGGREGATOR_H +#define MINAGGREGATOR_H + +#include "livestatus/table.hpp" +#include "livestatus/aggregator.hpp" +#include <cfloat> + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +struct MinAggregatorState final : public AggregatorState +{ + double Min{DBL_MAX}; +}; + +/** + * @ingroup livestatus + */ +class MinAggregator final : public Aggregator +{ +public: + DECLARE_PTR_TYPEDEFS(MinAggregator); + + MinAggregator(String attr); + + void Apply(const Table::Ptr& table, const Value& row, AggregatorState **state) override; + double GetResultAndFreeState(AggregatorState *state) const override; + +private: + String m_MinAttr; + + static MinAggregatorState *EnsureState(AggregatorState **state); +}; + +} + +#endif /* MINAGGREGATOR_H */ diff --git a/lib/livestatus/negatefilter.cpp b/lib/livestatus/negatefilter.cpp new file mode 100644 index 0000000..60202b4 --- /dev/null +++ b/lib/livestatus/negatefilter.cpp @@ -0,0 +1,14 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/negatefilter.hpp" + +using namespace icinga; + +NegateFilter::NegateFilter(Filter::Ptr inner) + : m_Inner(std::move(inner)) +{ } + +bool NegateFilter::Apply(const Table::Ptr& table, const Value& row) +{ + return !m_Inner->Apply(table, row); +} diff --git a/lib/livestatus/negatefilter.hpp b/lib/livestatus/negatefilter.hpp new file mode 100644 index 0000000..c08943c --- /dev/null +++ b/lib/livestatus/negatefilter.hpp @@ -0,0 +1,31 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef NEGATEFILTER_H +#define NEGATEFILTER_H + +#include "livestatus/filter.hpp" + +using namespace icinga; + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +class NegateFilter final : public Filter +{ +public: + DECLARE_PTR_TYPEDEFS(NegateFilter); + + NegateFilter(Filter::Ptr inner); + + bool Apply(const Table::Ptr& table, const Value& row) override; + +private: + Filter::Ptr m_Inner; +}; + +} + +#endif /* NEGATEFILTER_H */ diff --git a/lib/livestatus/orfilter.cpp b/lib/livestatus/orfilter.cpp new file mode 100644 index 0000000..6cc446c --- /dev/null +++ b/lib/livestatus/orfilter.cpp @@ -0,0 +1,18 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/orfilter.hpp" + +using namespace icinga; + +bool OrFilter::Apply(const Table::Ptr& table, const Value& row) +{ + if (m_Filters.empty()) + return true; + + for (const Filter::Ptr& filter : m_Filters) { + if (filter->Apply(table, row)) + return true; + } + + return false; +} diff --git a/lib/livestatus/orfilter.hpp b/lib/livestatus/orfilter.hpp new file mode 100644 index 0000000..df855c1 --- /dev/null +++ b/lib/livestatus/orfilter.hpp @@ -0,0 +1,26 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef ORFILTER_H +#define ORFILTER_H + +#include "livestatus/combinerfilter.hpp" + +using namespace icinga; + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +class OrFilter final : public CombinerFilter +{ +public: + DECLARE_PTR_TYPEDEFS(OrFilter); + + bool Apply(const Table::Ptr& table, const Value& row) override; +}; + +} + +#endif /* ORFILTER_H */ diff --git a/lib/livestatus/servicegroupstable.cpp b/lib/livestatus/servicegroupstable.cpp new file mode 100644 index 0000000..38d6d05 --- /dev/null +++ b/lib/livestatus/servicegroupstable.cpp @@ -0,0 +1,323 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/servicegroupstable.hpp" +#include "icinga/servicegroup.hpp" +#include "base/configtype.hpp" + +using namespace icinga; + +ServiceGroupsTable::ServiceGroupsTable() +{ + AddColumns(this); +} + +void ServiceGroupsTable::AddColumns(Table *table, const String& prefix, + const Column::ObjectAccessor& objectAccessor) +{ + table->AddColumn(prefix + "name", Column(&ServiceGroupsTable::NameAccessor, objectAccessor)); + table->AddColumn(prefix + "alias", Column(&ServiceGroupsTable::AliasAccessor, objectAccessor)); + table->AddColumn(prefix + "notes", Column(&ServiceGroupsTable::NotesAccessor, objectAccessor)); + table->AddColumn(prefix + "notes_url", Column(&ServiceGroupsTable::NotesUrlAccessor, objectAccessor)); + table->AddColumn(prefix + "action_url", Column(&ServiceGroupsTable::ActionUrlAccessor, objectAccessor)); + table->AddColumn(prefix + "members", Column(&ServiceGroupsTable::MembersAccessor, objectAccessor)); + table->AddColumn(prefix + "members_with_state", Column(&ServiceGroupsTable::MembersWithStateAccessor, objectAccessor)); + table->AddColumn(prefix + "worst_service_state", Column(&ServiceGroupsTable::WorstServiceStateAccessor, objectAccessor)); + table->AddColumn(prefix + "num_services", Column(&ServiceGroupsTable::NumServicesAccessor, objectAccessor)); + table->AddColumn(prefix + "num_services_ok", Column(&ServiceGroupsTable::NumServicesOkAccessor, objectAccessor)); + table->AddColumn(prefix + "num_services_warn", Column(&ServiceGroupsTable::NumServicesWarnAccessor, objectAccessor)); + table->AddColumn(prefix + "num_services_crit", Column(&ServiceGroupsTable::NumServicesCritAccessor, objectAccessor)); + table->AddColumn(prefix + "num_services_unknown", Column(&ServiceGroupsTable::NumServicesUnknownAccessor, objectAccessor)); + table->AddColumn(prefix + "num_services_pending", Column(&ServiceGroupsTable::NumServicesPendingAccessor, objectAccessor)); + table->AddColumn(prefix + "num_services_hard_ok", Column(&ServiceGroupsTable::NumServicesHardOkAccessor, objectAccessor)); + table->AddColumn(prefix + "num_services_hard_warn", Column(&ServiceGroupsTable::NumServicesHardWarnAccessor, objectAccessor)); + table->AddColumn(prefix + "num_services_hard_crit", Column(&ServiceGroupsTable::NumServicesHardCritAccessor, objectAccessor)); + table->AddColumn(prefix + "num_services_hard_unknown", Column(&ServiceGroupsTable::NumServicesHardUnknownAccessor, objectAccessor)); +} + +String ServiceGroupsTable::GetName() const +{ + return "servicegroups"; +} + +String ServiceGroupsTable::GetPrefix() const +{ + return "servicegroup"; +} + +void ServiceGroupsTable::FetchRows(const AddRowFunction& addRowFn) +{ + for (const ServiceGroup::Ptr& sg : ConfigType::GetObjectsByType<ServiceGroup>()) { + if (!addRowFn(sg, LivestatusGroupByNone, Empty)) + return; + } +} + +Value ServiceGroupsTable::NameAccessor(const Value& row) +{ + ServiceGroup::Ptr sg = static_cast<ServiceGroup::Ptr>(row); + + if (!sg) + return Empty; + + return sg->GetName(); +} + +Value ServiceGroupsTable::AliasAccessor(const Value& row) +{ + ServiceGroup::Ptr sg = static_cast<ServiceGroup::Ptr>(row); + + if (!sg) + return Empty; + + return sg->GetDisplayName(); +} + +Value ServiceGroupsTable::NotesAccessor(const Value& row) +{ + ServiceGroup::Ptr sg = static_cast<ServiceGroup::Ptr>(row); + + if (!sg) + return Empty; + + return sg->GetNotes(); +} + +Value ServiceGroupsTable::NotesUrlAccessor(const Value& row) +{ + ServiceGroup::Ptr sg = static_cast<ServiceGroup::Ptr>(row); + + if (!sg) + return Empty; + + return sg->GetNotesUrl(); +} + +Value ServiceGroupsTable::ActionUrlAccessor(const Value& row) +{ + ServiceGroup::Ptr sg = static_cast<ServiceGroup::Ptr>(row); + + if (!sg) + return Empty; + + return sg->GetActionUrl(); +} + +Value ServiceGroupsTable::MembersAccessor(const Value& row) +{ + ServiceGroup::Ptr sg = static_cast<ServiceGroup::Ptr>(row); + + if (!sg) + return Empty; + + ArrayData result; + + for (const Service::Ptr& service : sg->GetMembers()) { + result.push_back(new Array({ + service->GetHost()->GetName(), + service->GetShortName() + })); + } + + return new Array(std::move(result)); +} + +Value ServiceGroupsTable::MembersWithStateAccessor(const Value& row) +{ + ServiceGroup::Ptr sg = static_cast<ServiceGroup::Ptr>(row); + + if (!sg) + return Empty; + + ArrayData result; + + for (const Service::Ptr& service : sg->GetMembers()) { + result.push_back(new Array({ + service->GetHost()->GetName(), + service->GetShortName(), + service->GetHost()->GetState(), + service->GetState() + })); + } + + return new Array(std::move(result)); +} + +Value ServiceGroupsTable::WorstServiceStateAccessor(const Value& row) +{ + ServiceGroup::Ptr sg = static_cast<ServiceGroup::Ptr>(row); + + if (!sg) + return Empty; + + Value worst_service = ServiceOK; + + for (const Service::Ptr& service : sg->GetMembers()) { + if (service->GetState() > worst_service) + worst_service = service->GetState(); + } + + return worst_service; +} + +Value ServiceGroupsTable::NumServicesAccessor(const Value& row) +{ + ServiceGroup::Ptr sg = static_cast<ServiceGroup::Ptr>(row); + + if (!sg) + return Empty; + + return sg->GetMembers().size(); +} + +Value ServiceGroupsTable::NumServicesOkAccessor(const Value& row) +{ + ServiceGroup::Ptr sg = static_cast<ServiceGroup::Ptr>(row); + + if (!sg) + return Empty; + + int num_services = 0; + + for (const Service::Ptr& service : sg->GetMembers()) { + if (service->GetState() == ServiceOK) + num_services++; + } + + return num_services; +} + +Value ServiceGroupsTable::NumServicesWarnAccessor(const Value& row) +{ + ServiceGroup::Ptr sg = static_cast<ServiceGroup::Ptr>(row); + + if (!sg) + return Empty; + + int num_services = 0; + + for (const Service::Ptr& service : sg->GetMembers()) { + if (service->GetState() == ServiceWarning) + num_services++; + } + + return num_services; +} + +Value ServiceGroupsTable::NumServicesCritAccessor(const Value& row) +{ + ServiceGroup::Ptr sg = static_cast<ServiceGroup::Ptr>(row); + + if (!sg) + return Empty; + + int num_services = 0; + + for (const Service::Ptr& service : sg->GetMembers()) { + if (service->GetState() == ServiceCritical) + num_services++; + } + + return num_services; +} + +Value ServiceGroupsTable::NumServicesUnknownAccessor(const Value& row) +{ + ServiceGroup::Ptr sg = static_cast<ServiceGroup::Ptr>(row); + + if (!sg) + return Empty; + + int num_services = 0; + + for (const Service::Ptr& service : sg->GetMembers()) { + if (service->GetState() == ServiceUnknown) + num_services++; + } + + return num_services; +} + +Value ServiceGroupsTable::NumServicesPendingAccessor(const Value& row) +{ + ServiceGroup::Ptr sg = static_cast<ServiceGroup::Ptr>(row); + + if (!sg) + return Empty; + + int num_services = 0; + + for (const Service::Ptr& service : sg->GetMembers()) { + if (!service->GetLastCheckResult()) + num_services++; + } + + return num_services; +} + +Value ServiceGroupsTable::NumServicesHardOkAccessor(const Value& row) +{ + ServiceGroup::Ptr sg = static_cast<ServiceGroup::Ptr>(row); + + if (!sg) + return Empty; + + int num_services = 0; + + for (const Service::Ptr& service : sg->GetMembers()) { + if (service->GetStateType() == StateTypeHard && service->GetState() == ServiceOK) + num_services++; + } + + return num_services; +} + +Value ServiceGroupsTable::NumServicesHardWarnAccessor(const Value& row) +{ + ServiceGroup::Ptr sg = static_cast<ServiceGroup::Ptr>(row); + + if (!sg) + return Empty; + + int num_services = 0; + + for (const Service::Ptr& service : sg->GetMembers()) { + if (service->GetStateType() == StateTypeHard && service->GetState() == ServiceWarning) + num_services++; + } + + return num_services; +} + +Value ServiceGroupsTable::NumServicesHardCritAccessor(const Value& row) +{ + ServiceGroup::Ptr sg = static_cast<ServiceGroup::Ptr>(row); + + if (!sg) + return Empty; + + int num_services = 0; + + for (const Service::Ptr& service : sg->GetMembers()) { + if (service->GetStateType() == StateTypeHard && service->GetState() == ServiceCritical) + num_services++; + } + + return num_services; +} + +Value ServiceGroupsTable::NumServicesHardUnknownAccessor(const Value& row) +{ + ServiceGroup::Ptr sg = static_cast<ServiceGroup::Ptr>(row); + + if (!sg) + return Empty; + + int num_services = 0; + + for (const Service::Ptr& service : sg->GetMembers()) { + if (service->GetStateType() == StateTypeHard && service->GetState() == ServiceUnknown) + num_services++; + } + + return num_services; +} diff --git a/lib/livestatus/servicegroupstable.hpp b/lib/livestatus/servicegroupstable.hpp new file mode 100644 index 0000000..b3c60c4 --- /dev/null +++ b/lib/livestatus/servicegroupstable.hpp @@ -0,0 +1,54 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef SERVICEGROUPSTABLE_H +#define SERVICEGROUPSTABLE_H + +#include "livestatus/table.hpp" + +using namespace icinga; + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +class ServiceGroupsTable final : public Table +{ +public: + DECLARE_PTR_TYPEDEFS(ServiceGroupsTable); + + ServiceGroupsTable(); + + static void AddColumns(Table *table, const String& prefix = String(), + const Column::ObjectAccessor& objectAccessor = Column::ObjectAccessor()); + + String GetName() const override; + String GetPrefix() const override; + +protected: + void FetchRows(const AddRowFunction& addRowFn) override; + + static Value NameAccessor(const Value& row); + static Value AliasAccessor(const Value& row); + static Value NotesAccessor(const Value& row); + static Value NotesUrlAccessor(const Value& row); + static Value ActionUrlAccessor(const Value& row); + static Value MembersAccessor(const Value& row); + static Value MembersWithStateAccessor(const Value& row); + static Value WorstServiceStateAccessor(const Value& row); + static Value NumServicesAccessor(const Value& row); + static Value NumServicesOkAccessor(const Value& row); + static Value NumServicesWarnAccessor(const Value& row); + static Value NumServicesCritAccessor(const Value& row); + static Value NumServicesUnknownAccessor(const Value& row); + static Value NumServicesPendingAccessor(const Value& row); + static Value NumServicesHardOkAccessor(const Value& row); + static Value NumServicesHardWarnAccessor(const Value& row); + static Value NumServicesHardCritAccessor(const Value& row); + static Value NumServicesHardUnknownAccessor(const Value& row); +}; + +} + +#endif /* SERVICEGROUPSTABLE_H */ diff --git a/lib/livestatus/servicestable.cpp b/lib/livestatus/servicestable.cpp new file mode 100644 index 0000000..681445a --- /dev/null +++ b/lib/livestatus/servicestable.cpp @@ -0,0 +1,1200 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/servicestable.hpp" +#include "livestatus/hoststable.hpp" +#include "livestatus/servicegroupstable.hpp" +#include "livestatus/hostgroupstable.hpp" +#include "livestatus/endpointstable.hpp" +#include "icinga/service.hpp" +#include "icinga/servicegroup.hpp" +#include "icinga/hostgroup.hpp" +#include "icinga/checkcommand.hpp" +#include "icinga/eventcommand.hpp" +#include "icinga/timeperiod.hpp" +#include "icinga/macroprocessor.hpp" +#include "icinga/compatutility.hpp" +#include "icinga/pluginutility.hpp" +#include "base/configtype.hpp" +#include "base/objectlock.hpp" +#include "base/json.hpp" +#include "base/convert.hpp" +#include "base/utility.hpp" +#include <boost/algorithm/string/replace.hpp> + +using namespace icinga; + +ServicesTable::ServicesTable(LivestatusGroupByType type) + : Table(type) +{ + AddColumns(this); +} + + +void ServicesTable::AddColumns(Table *table, const String& prefix, + const Column::ObjectAccessor& objectAccessor) +{ + table->AddColumn(prefix + "description", Column(&ServicesTable::ShortNameAccessor, objectAccessor)); + table->AddColumn(prefix + "service_description", Column(&ServicesTable::ShortNameAccessor, objectAccessor)); //ugly compatibility hack + table->AddColumn(prefix + "display_name", Column(&ServicesTable::DisplayNameAccessor, objectAccessor)); + table->AddColumn(prefix + "check_command", Column(&ServicesTable::CheckCommandAccessor, objectAccessor)); + table->AddColumn(prefix + "check_command_expanded", Column(&ServicesTable::CheckCommandExpandedAccessor, objectAccessor)); + table->AddColumn(prefix + "event_handler", Column(&ServicesTable::EventHandlerAccessor, objectAccessor)); + table->AddColumn(prefix + "plugin_output", Column(&ServicesTable::PluginOutputAccessor, objectAccessor)); + table->AddColumn(prefix + "long_plugin_output", Column(&ServicesTable::LongPluginOutputAccessor, objectAccessor)); + table->AddColumn(prefix + "perf_data", Column(&ServicesTable::PerfDataAccessor, objectAccessor)); + table->AddColumn(prefix + "notification_period", Column(&Table::EmptyStringAccessor, objectAccessor)); + table->AddColumn(prefix + "check_period", Column(&ServicesTable::CheckPeriodAccessor, objectAccessor)); + table->AddColumn(prefix + "notes", Column(&ServicesTable::NotesAccessor, objectAccessor)); + table->AddColumn(prefix + "notes_expanded", Column(&ServicesTable::NotesExpandedAccessor, objectAccessor)); + table->AddColumn(prefix + "notes_url", Column(&ServicesTable::NotesUrlAccessor, objectAccessor)); + table->AddColumn(prefix + "notes_url_expanded", Column(&ServicesTable::NotesUrlExpandedAccessor, objectAccessor)); + table->AddColumn(prefix + "action_url", Column(&ServicesTable::ActionUrlAccessor, objectAccessor)); + table->AddColumn(prefix + "action_url_expanded", Column(&ServicesTable::ActionUrlExpandedAccessor, objectAccessor)); + table->AddColumn(prefix + "icon_image", Column(&ServicesTable::IconImageAccessor, objectAccessor)); + table->AddColumn(prefix + "icon_image_expanded", Column(&ServicesTable::IconImageExpandedAccessor, objectAccessor)); + table->AddColumn(prefix + "icon_image_alt", Column(&ServicesTable::IconImageAltAccessor, objectAccessor)); + table->AddColumn(prefix + "initial_state", Column(&Table::EmptyStringAccessor, objectAccessor)); + table->AddColumn(prefix + "max_check_attempts", Column(&ServicesTable::MaxCheckAttemptsAccessor, objectAccessor)); + table->AddColumn(prefix + "current_attempt", Column(&ServicesTable::CurrentAttemptAccessor, objectAccessor)); + table->AddColumn(prefix + "state", Column(&ServicesTable::StateAccessor, objectAccessor)); + table->AddColumn(prefix + "has_been_checked", Column(&ServicesTable::HasBeenCheckedAccessor, objectAccessor)); + table->AddColumn(prefix + "last_state", Column(&ServicesTable::LastStateAccessor, objectAccessor)); + table->AddColumn(prefix + "last_hard_state", Column(&ServicesTable::LastHardStateAccessor, objectAccessor)); + table->AddColumn(prefix + "state_type", Column(&ServicesTable::StateTypeAccessor, objectAccessor)); + table->AddColumn(prefix + "check_type", Column(&ServicesTable::CheckTypeAccessor, objectAccessor)); + table->AddColumn(prefix + "acknowledged", Column(&ServicesTable::AcknowledgedAccessor, objectAccessor)); + table->AddColumn(prefix + "acknowledgement_type", Column(&ServicesTable::AcknowledgementTypeAccessor, objectAccessor)); + table->AddColumn(prefix + "no_more_notifications", Column(&ServicesTable::NoMoreNotificationsAccessor, objectAccessor)); + table->AddColumn(prefix + "last_time_ok", Column(&ServicesTable::LastTimeOkAccessor, objectAccessor)); + table->AddColumn(prefix + "last_time_warning", Column(&ServicesTable::LastTimeWarningAccessor, objectAccessor)); + table->AddColumn(prefix + "last_time_critical", Column(&ServicesTable::LastTimeCriticalAccessor, objectAccessor)); + table->AddColumn(prefix + "last_time_unknown", Column(&ServicesTable::LastTimeUnknownAccessor, objectAccessor)); + table->AddColumn(prefix + "last_check", Column(&ServicesTable::LastCheckAccessor, objectAccessor)); + table->AddColumn(prefix + "next_check", Column(&ServicesTable::NextCheckAccessor, objectAccessor)); + table->AddColumn(prefix + "last_notification", Column(&ServicesTable::LastNotificationAccessor, objectAccessor)); + table->AddColumn(prefix + "next_notification", Column(&ServicesTable::NextNotificationAccessor, objectAccessor)); + table->AddColumn(prefix + "current_notification_number", Column(&ServicesTable::CurrentNotificationNumberAccessor, objectAccessor)); + table->AddColumn(prefix + "last_state_change", Column(&ServicesTable::LastStateChangeAccessor, objectAccessor)); + table->AddColumn(prefix + "last_hard_state_change", Column(&ServicesTable::LastHardStateChangeAccessor, objectAccessor)); + table->AddColumn(prefix + "scheduled_downtime_depth", Column(&ServicesTable::ScheduledDowntimeDepthAccessor, objectAccessor)); + table->AddColumn(prefix + "is_flapping", Column(&ServicesTable::IsFlappingAccessor, objectAccessor)); + table->AddColumn(prefix + "checks_enabled", Column(&ServicesTable::ChecksEnabledAccessor, objectAccessor)); + table->AddColumn(prefix + "accept_passive_checks", Column(&ServicesTable::AcceptPassiveChecksAccessor, objectAccessor)); + table->AddColumn(prefix + "event_handler_enabled", Column(&ServicesTable::EventHandlerEnabledAccessor, objectAccessor)); + table->AddColumn(prefix + "notifications_enabled", Column(&ServicesTable::NotificationsEnabledAccessor, objectAccessor)); + table->AddColumn(prefix + "process_performance_data", Column(&ServicesTable::ProcessPerformanceDataAccessor, objectAccessor)); + table->AddColumn(prefix + "is_executing", Column(&Table::ZeroAccessor, objectAccessor)); + table->AddColumn(prefix + "active_checks_enabled", Column(&ServicesTable::ActiveChecksEnabledAccessor, objectAccessor)); + table->AddColumn(prefix + "check_options", Column(&Table::EmptyStringAccessor, objectAccessor)); + table->AddColumn(prefix + "flap_detection_enabled", Column(&ServicesTable::FlapDetectionEnabledAccessor, objectAccessor)); + table->AddColumn(prefix + "check_freshness", Column(&Table::OneAccessor, objectAccessor)); + table->AddColumn(prefix + "obsess_over_service", Column(&Table::ZeroAccessor, objectAccessor)); + table->AddColumn(prefix + "modified_attributes", Column(&Table::ZeroAccessor, objectAccessor)); + table->AddColumn(prefix + "modified_attributes_list", Column(&Table::ZeroAccessor, objectAccessor)); + table->AddColumn(prefix + "pnpgraph_present", Column(&Table::ZeroAccessor, objectAccessor)); + table->AddColumn(prefix + "staleness", Column(&ServicesTable::StalenessAccessor, objectAccessor)); + table->AddColumn(prefix + "check_interval", Column(&ServicesTable::CheckIntervalAccessor, objectAccessor)); + table->AddColumn(prefix + "retry_interval", Column(&ServicesTable::RetryIntervalAccessor, objectAccessor)); + table->AddColumn(prefix + "notification_interval", Column(&ServicesTable::NotificationIntervalAccessor, objectAccessor)); + table->AddColumn(prefix + "first_notification_delay", Column(&Table::EmptyStringAccessor, objectAccessor)); + table->AddColumn(prefix + "low_flap_threshold", Column(&ServicesTable::LowFlapThresholdAccessor, objectAccessor)); + table->AddColumn(prefix + "high_flap_threshold", Column(&ServicesTable::HighFlapThresholdAccessor, objectAccessor)); + table->AddColumn(prefix + "latency", Column(&ServicesTable::LatencyAccessor, objectAccessor)); + table->AddColumn(prefix + "execution_time", Column(&ServicesTable::ExecutionTimeAccessor, objectAccessor)); + table->AddColumn(prefix + "percent_state_change", Column(&ServicesTable::PercentStateChangeAccessor, objectAccessor)); + table->AddColumn(prefix + "in_check_period", Column(&ServicesTable::InCheckPeriodAccessor, objectAccessor)); + table->AddColumn(prefix + "in_notification_period", Column(&ServicesTable::InNotificationPeriodAccessor, objectAccessor)); + table->AddColumn(prefix + "contacts", Column(&ServicesTable::ContactsAccessor, objectAccessor)); + table->AddColumn(prefix + "downtimes", Column(&ServicesTable::DowntimesAccessor, objectAccessor)); + table->AddColumn(prefix + "downtimes_with_info", Column(&ServicesTable::DowntimesWithInfoAccessor, objectAccessor)); + table->AddColumn(prefix + "comments", Column(&ServicesTable::CommentsAccessor, objectAccessor)); + table->AddColumn(prefix + "comments_with_info", Column(&ServicesTable::CommentsWithInfoAccessor, objectAccessor)); + table->AddColumn(prefix + "comments_with_extra_info", Column(&ServicesTable::CommentsWithExtraInfoAccessor, objectAccessor)); + table->AddColumn(prefix + "custom_variable_names", Column(&ServicesTable::CustomVariableNamesAccessor, objectAccessor)); + table->AddColumn(prefix + "custom_variable_values", Column(&ServicesTable::CustomVariableValuesAccessor, objectAccessor)); + table->AddColumn(prefix + "custom_variables", Column(&ServicesTable::CustomVariablesAccessor, objectAccessor)); + table->AddColumn(prefix + "groups", Column(&ServicesTable::GroupsAccessor, objectAccessor)); + table->AddColumn(prefix + "contact_groups", Column(&ServicesTable::ContactGroupsAccessor, objectAccessor)); + table->AddColumn(prefix + "check_source", Column(&ServicesTable::CheckSourceAccessor, objectAccessor)); + table->AddColumn(prefix + "is_reachable", Column(&ServicesTable::IsReachableAccessor, objectAccessor)); + table->AddColumn(prefix + "cv_is_json", Column(&ServicesTable::CVIsJsonAccessor, objectAccessor)); + table->AddColumn(prefix + "original_attributes", Column(&ServicesTable::OriginalAttributesAccessor, objectAccessor)); + + HostsTable::AddColumns(table, "host_", [objectAccessor](const Value& row, LivestatusGroupByType, const Object::Ptr&) -> Value { + return HostAccessor(row, objectAccessor); + }); + + /* add additional group by values received through the object accessor */ + if (table->GetGroupByType() == LivestatusGroupByServiceGroup) { + /* _1 = row, _2 = groupByType, _3 = groupByObject */ + Log(LogDebug, "Livestatus") + << "Processing services group by servicegroup table."; + ServiceGroupsTable::AddColumns(table, "servicegroup_", [](const Value& row, LivestatusGroupByType groupByType, const Object::Ptr& groupByObject) -> Value { + return ServiceGroupAccessor(row, groupByType, groupByObject); + }); + } else if (table->GetGroupByType() == LivestatusGroupByHostGroup) { + /* _1 = row, _2 = groupByType, _3 = groupByObject */ + Log(LogDebug, "Livestatus") + << "Processing services group by hostgroup table."; + HostGroupsTable::AddColumns(table, "hostgroup_", [](const Value& row, LivestatusGroupByType groupByType, const Object::Ptr& groupByObject) -> Value { + return HostGroupAccessor(row, groupByType, groupByObject); + }); + } +} + +String ServicesTable::GetName() const +{ + return "services"; +} + +String ServicesTable::GetPrefix() const +{ + return "service"; +} + +void ServicesTable::FetchRows(const AddRowFunction& addRowFn) +{ + if (GetGroupByType() == LivestatusGroupByServiceGroup) { + for (const ServiceGroup::Ptr& sg : ConfigType::GetObjectsByType<ServiceGroup>()) { + for (const Service::Ptr& service : sg->GetMembers()) { + /* the caller must know which groupby type and value are set for this row */ + if (!addRowFn(service, LivestatusGroupByServiceGroup, sg)) + return; + } + } + } else if (GetGroupByType() == LivestatusGroupByHostGroup) { + for (const HostGroup::Ptr& hg : ConfigType::GetObjectsByType<HostGroup>()) { + ObjectLock ylock(hg); + for (const Host::Ptr& host : hg->GetMembers()) { + ObjectLock ylock(host); + for (const Service::Ptr& service : host->GetServices()) { + /* the caller must know which groupby type and value are set for this row */ + if (!addRowFn(service, LivestatusGroupByHostGroup, hg)) + return; + } + } + } + } else { + for (const Service::Ptr& service : ConfigType::GetObjectsByType<Service>()) { + if (!addRowFn(service, LivestatusGroupByNone, Empty)) + return; + } + } +} + +Object::Ptr ServicesTable::HostAccessor(const Value& row, const Column::ObjectAccessor& parentObjectAccessor) +{ + Value service; + + if (parentObjectAccessor) + service = parentObjectAccessor(row, LivestatusGroupByNone, Empty); + else + service = row; + + Service::Ptr svc = static_cast<Service::Ptr>(service); + + if (!svc) + return nullptr; + + return svc->GetHost(); +} + +Object::Ptr ServicesTable::ServiceGroupAccessor(const Value& row, LivestatusGroupByType groupByType, const Object::Ptr& groupByObject) +{ + /* return the current group by value set from within FetchRows() + * this is the servicegroup object used for the table join inside + * in AddColumns() + */ + if (groupByType == LivestatusGroupByServiceGroup) + return groupByObject; + + return nullptr; +} + +Object::Ptr ServicesTable::HostGroupAccessor(const Value& row, LivestatusGroupByType groupByType, const Object::Ptr& groupByObject) +{ + /* return the current group by value set from within FetchRows() + * this is the servicegroup object used for the table join inside + * in AddColumns() + */ + if (groupByType == LivestatusGroupByHostGroup) + return groupByObject; + + return nullptr; +} + +Value ServicesTable::ShortNameAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return service->GetShortName(); +} + +Value ServicesTable::DisplayNameAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return service->GetDisplayName(); +} + +Value ServicesTable::CheckCommandAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + CheckCommand::Ptr checkcommand = service->GetCheckCommand(); + + if (checkcommand) + return CompatUtility::GetCommandName(checkcommand) + "!" + CompatUtility::GetCheckableCommandArgs(service); + + return Empty; +} + +Value ServicesTable::CheckCommandExpandedAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + CheckCommand::Ptr checkcommand = service->GetCheckCommand(); + + if (checkcommand) + return CompatUtility::GetCommandName(checkcommand) + "!" + CompatUtility::GetCheckableCommandArgs(service); + + return Empty; +} + +Value ServicesTable::EventHandlerAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + EventCommand::Ptr eventcommand = service->GetEventCommand(); + + if (eventcommand) + return CompatUtility::GetCommandName(eventcommand); + + return Empty; +} + +Value ServicesTable::PluginOutputAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + String output; + CheckResult::Ptr cr = service->GetLastCheckResult(); + + if (cr) + output = CompatUtility::GetCheckResultOutput(cr); + + return output; +} + +Value ServicesTable::LongPluginOutputAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + String long_output; + CheckResult::Ptr cr = service->GetLastCheckResult(); + + if (cr) + long_output = CompatUtility::GetCheckResultLongOutput(cr); + + return long_output; +} + +Value ServicesTable::PerfDataAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + String perfdata; + CheckResult::Ptr cr = service->GetLastCheckResult(); + + if (!cr) + return Empty; + + return PluginUtility::FormatPerfdata(cr->GetPerformanceData()); +} + +Value ServicesTable::CheckPeriodAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + TimePeriod::Ptr checkPeriod = service->GetCheckPeriod(); + + if (!checkPeriod) + return Empty; + + return checkPeriod->GetName(); +} + +Value ServicesTable::NotesAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return service->GetNotes(); +} + +Value ServicesTable::NotesExpandedAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + MacroProcessor::ResolverList resolvers { + { "service", service }, + { "host", service->GetHost() }, + }; + + return MacroProcessor::ResolveMacros(service->GetNotes(), resolvers); +} + +Value ServicesTable::NotesUrlAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return service->GetNotesUrl(); +} + +Value ServicesTable::NotesUrlExpandedAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + MacroProcessor::ResolverList resolvers { + { "service", service }, + { "host", service->GetHost() }, + }; + + return MacroProcessor::ResolveMacros(service->GetNotesUrl(), resolvers); +} + +Value ServicesTable::ActionUrlAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return service->GetActionUrl(); +} + +Value ServicesTable::ActionUrlExpandedAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + MacroProcessor::ResolverList resolvers { + { "service", service }, + { "host", service->GetHost() }, + }; + + return MacroProcessor::ResolveMacros(service->GetActionUrl(), resolvers); +} + +Value ServicesTable::IconImageAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return service->GetIconImage(); +} + +Value ServicesTable::IconImageExpandedAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + MacroProcessor::ResolverList resolvers { + { "service", service }, + { "host", service->GetHost() }, + }; + + return MacroProcessor::ResolveMacros(service->GetIconImage(), resolvers); +} + +Value ServicesTable::IconImageAltAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return service->GetIconImageAlt(); +} + +Value ServicesTable::MaxCheckAttemptsAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return service->GetMaxCheckAttempts(); +} + +Value ServicesTable::CurrentAttemptAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return service->GetCheckAttempt(); +} + +Value ServicesTable::StateAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return service->GetState(); +} + +Value ServicesTable::HasBeenCheckedAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return Convert::ToLong(service->HasBeenChecked()); +} + +Value ServicesTable::LastStateAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return service->GetLastState(); +} + +Value ServicesTable::LastHardStateAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return service->GetLastHardState(); +} + +Value ServicesTable::StateTypeAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return service->GetStateType(); +} + +Value ServicesTable::CheckTypeAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return (service->GetEnableActiveChecks() ? 0 : 1); /* 0 .. active, 1 .. passive */ +} + +Value ServicesTable::AcknowledgedAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + ObjectLock olock(service); + return service->IsAcknowledged(); +} + +Value ServicesTable::AcknowledgementTypeAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + ObjectLock olock(service); + return service->GetAcknowledgement(); +} + +Value ServicesTable::NoMoreNotificationsAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return (CompatUtility::GetCheckableNotificationNotificationInterval(service) == 0 && !service->GetVolatile()) ? 1 : 0; +} + +Value ServicesTable::LastTimeOkAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return static_cast<int>(service->GetLastStateOK()); +} + +Value ServicesTable::LastTimeWarningAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return static_cast<int>(service->GetLastStateWarning()); +} + +Value ServicesTable::LastTimeCriticalAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return static_cast<int>(service->GetLastStateCritical()); +} + +Value ServicesTable::LastTimeUnknownAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return static_cast<int>(service->GetLastStateUnknown()); +} + +Value ServicesTable::LastCheckAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return static_cast<int>(service->GetLastCheck()); +} + +Value ServicesTable::NextCheckAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return static_cast<int>(service->GetNextCheck()); +} + +Value ServicesTable::LastNotificationAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return CompatUtility::GetCheckableNotificationLastNotification(service); +} + +Value ServicesTable::NextNotificationAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return CompatUtility::GetCheckableNotificationNextNotification(service); +} + +Value ServicesTable::CurrentNotificationNumberAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return CompatUtility::GetCheckableNotificationNotificationNumber(service); +} + +Value ServicesTable::LastStateChangeAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return static_cast<int>(service->GetLastStateChange()); +} + +Value ServicesTable::LastHardStateChangeAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return static_cast<int>(service->GetLastHardStateChange()); +} + +Value ServicesTable::ScheduledDowntimeDepthAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return service->GetDowntimeDepth(); +} + +Value ServicesTable::IsFlappingAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return service->IsFlapping(); +} + +Value ServicesTable::ChecksEnabledAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return Convert::ToLong(service->GetEnableActiveChecks()); +} + +Value ServicesTable::AcceptPassiveChecksAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return Convert::ToLong(service->GetEnablePassiveChecks()); +} + +Value ServicesTable::EventHandlerEnabledAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return Convert::ToLong(service->GetEnableEventHandler()); +} + +Value ServicesTable::NotificationsEnabledAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return Convert::ToLong(service->GetEnableNotifications()); +} + +Value ServicesTable::ProcessPerformanceDataAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return Convert::ToLong(service->GetEnablePerfdata()); +} + +Value ServicesTable::ActiveChecksEnabledAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return Convert::ToLong(service->GetEnableActiveChecks()); +} + +Value ServicesTable::FlapDetectionEnabledAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return Convert::ToLong(service->GetEnableFlapping()); +} + +Value ServicesTable::StalenessAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + if (service->HasBeenChecked() && service->GetLastCheck() > 0) + return (Utility::GetTime() - service->GetLastCheck()) / (service->GetCheckInterval() * 3600); + + return 0.0; +} + +Value ServicesTable::CheckIntervalAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return service->GetCheckInterval() / LIVESTATUS_INTERVAL_LENGTH; +} + +Value ServicesTable::RetryIntervalAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return service->GetRetryInterval() / LIVESTATUS_INTERVAL_LENGTH; +} + +Value ServicesTable::NotificationIntervalAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return CompatUtility::GetCheckableNotificationNotificationInterval(service); +} + +Value ServicesTable::LowFlapThresholdAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return service->GetFlappingThresholdLow(); +} + +Value ServicesTable::HighFlapThresholdAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return service->GetFlappingThresholdHigh(); +} + +Value ServicesTable::LatencyAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + CheckResult::Ptr cr = service->GetLastCheckResult(); + + if (!cr) + return Empty; + + return cr->CalculateLatency(); +} + +Value ServicesTable::ExecutionTimeAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + CheckResult::Ptr cr = service->GetLastCheckResult(); + + if (!cr) + return Empty; + + return cr->CalculateExecutionTime(); +} + +Value ServicesTable::PercentStateChangeAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return service->GetFlappingCurrent(); +} + +Value ServicesTable::InCheckPeriodAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + TimePeriod::Ptr timeperiod = service->GetCheckPeriod(); + + /* none set means always checked */ + if (!timeperiod) + return 1; + + return Convert::ToLong(timeperiod->IsInside(Utility::GetTime())); +} + +Value ServicesTable::InNotificationPeriodAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + for (const Notification::Ptr& notification : service->GetNotifications()) { + TimePeriod::Ptr timeperiod = notification->GetPeriod(); + + if (!timeperiod || timeperiod->IsInside(Utility::GetTime())) + return 1; + } + + return 0; +} + +Value ServicesTable::ContactsAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + ArrayData result; + + for (const User::Ptr& user : CompatUtility::GetCheckableNotificationUsers(service)) { + result.push_back(user->GetName()); + } + + return new Array(std::move(result)); +} + +Value ServicesTable::DowntimesAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + ArrayData result; + + for (const Downtime::Ptr& downtime : service->GetDowntimes()) { + if (downtime->IsExpired()) + continue; + + result.push_back(downtime->GetLegacyId()); + } + + return new Array(std::move(result)); +} + +Value ServicesTable::DowntimesWithInfoAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + ArrayData result; + + for (const Downtime::Ptr& downtime : service->GetDowntimes()) { + if (downtime->IsExpired()) + continue; + + result.push_back(new Array({ + downtime->GetLegacyId(), + downtime->GetAuthor(), + downtime->GetComment() + })); + } + + return new Array(std::move(result)); +} + +Value ServicesTable::CommentsAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + ArrayData result; + + for (const Comment::Ptr& comment : service->GetComments()) { + if (comment->IsExpired()) + continue; + + result.push_back(comment->GetLegacyId()); + } + + return new Array(std::move(result)); +} + +Value ServicesTable::CommentsWithInfoAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + ArrayData result; + + for (const Comment::Ptr& comment : service->GetComments()) { + if (comment->IsExpired()) + continue; + + result.push_back(new Array({ + comment->GetLegacyId(), + comment->GetAuthor(), + comment->GetText() + })); + } + + return new Array(std::move(result)); +} + +Value ServicesTable::CommentsWithExtraInfoAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + ArrayData result; + + for (const Comment::Ptr& comment : service->GetComments()) { + if (comment->IsExpired()) + continue; + + result.push_back(new Array({ + comment->GetLegacyId(), + comment->GetAuthor(), + comment->GetText(), + comment->GetEntryType(), + static_cast<int>(comment->GetEntryTime()) + })); + } + + return new Array(std::move(result)); +} + +Value ServicesTable::CustomVariableNamesAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + Dictionary::Ptr vars = service->GetVars(); + + ArrayData result; + + if (vars) { + ObjectLock olock(vars); + for (const Dictionary::Pair& kv : vars) { + result.push_back(kv.first); + } + } + + return new Array(std::move(result)); +} + +Value ServicesTable::CustomVariableValuesAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + Dictionary::Ptr vars = service->GetVars(); + + ArrayData result; + + if (vars) { + ObjectLock olock(vars); + for (const Dictionary::Pair& kv : vars) { + if (kv.second.IsObjectType<Array>() || kv.second.IsObjectType<Dictionary>()) + result.push_back(JsonEncode(kv.second)); + else + result.push_back(kv.second); + } + } + + return new Array(std::move(result)); +} + +Value ServicesTable::CustomVariablesAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + Dictionary::Ptr vars = service->GetVars(); + + ArrayData result; + + if (vars) { + ObjectLock olock(vars); + for (const Dictionary::Pair& kv : vars) { + Value val; + + if (kv.second.IsObjectType<Array>() || kv.second.IsObjectType<Dictionary>()) + val = JsonEncode(kv.second); + else + val = kv.second; + + result.push_back(new Array({ + kv.first, + val + })); + } + } + + return new Array(std::move(result)); +} + +Value ServicesTable::CVIsJsonAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + Dictionary::Ptr vars = service->GetVars(); + + if (!vars) + return Empty; + + bool cv_is_json = false; + + ObjectLock olock(vars); + for (const Dictionary::Pair& kv : vars) { + if (kv.second.IsObjectType<Array>() || kv.second.IsObjectType<Dictionary>()) + cv_is_json = true; + } + + return cv_is_json; +} + +Value ServicesTable::GroupsAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + Array::Ptr groups = service->GetGroups(); + + if (!groups) + return Empty; + + return groups; +} + +Value ServicesTable::ContactGroupsAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + ArrayData result; + + for (const UserGroup::Ptr& usergroup : CompatUtility::GetCheckableNotificationUserGroups(service)) { + result.push_back(usergroup->GetName()); + } + + return new Array(std::move(result)); +} + +Value ServicesTable::CheckSourceAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + CheckResult::Ptr cr = service->GetLastCheckResult(); + + if (cr) + return cr->GetCheckSource(); + + return Empty; +} + +Value ServicesTable::IsReachableAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return service->IsReachable(); +} + +Value ServicesTable::OriginalAttributesAccessor(const Value& row) +{ + Service::Ptr service = static_cast<Service::Ptr>(row); + + if (!service) + return Empty; + + return JsonEncode(service->GetOriginalAttributes()); +} diff --git a/lib/livestatus/servicestable.hpp b/lib/livestatus/servicestable.hpp new file mode 100644 index 0000000..56d5ae5 --- /dev/null +++ b/lib/livestatus/servicestable.hpp @@ -0,0 +1,115 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef SERVICESTABLE_H +#define SERVICESTABLE_H + +#include "livestatus/table.hpp" + +using namespace icinga; + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +class ServicesTable final : public Table +{ +public: + DECLARE_PTR_TYPEDEFS(ServicesTable); + + ServicesTable(LivestatusGroupByType type = LivestatusGroupByNone); + + static void AddColumns(Table *table, const String& prefix = String(), + const Column::ObjectAccessor& objectAccessor = Column::ObjectAccessor()); + + String GetName() const override; + String GetPrefix() const override; + +protected: + void FetchRows(const AddRowFunction& addRowFn) override; + + static Object::Ptr HostAccessor(const Value& row, const Column::ObjectAccessor& parentObjectAccessor); + static Object::Ptr ServiceGroupAccessor(const Value& row, LivestatusGroupByType groupByType, const Object::Ptr& groupByObject); + static Object::Ptr HostGroupAccessor(const Value& row, LivestatusGroupByType groupByType, const Object::Ptr& groupByObject); + + static Value ShortNameAccessor(const Value& row); + static Value DisplayNameAccessor(const Value& row); + static Value CheckCommandAccessor(const Value& row); + static Value CheckCommandExpandedAccessor(const Value& row); + static Value EventHandlerAccessor(const Value& row); + static Value PluginOutputAccessor(const Value& row); + static Value LongPluginOutputAccessor(const Value& row); + static Value PerfDataAccessor(const Value& row); + static Value CheckPeriodAccessor(const Value& row); + static Value NotesAccessor(const Value& row); + static Value NotesExpandedAccessor(const Value& row); + static Value NotesUrlAccessor(const Value& row); + static Value NotesUrlExpandedAccessor(const Value& row); + static Value ActionUrlAccessor(const Value& row); + static Value ActionUrlExpandedAccessor(const Value& row); + static Value IconImageAccessor(const Value& row); + static Value IconImageExpandedAccessor(const Value& row); + static Value IconImageAltAccessor(const Value& row); + static Value MaxCheckAttemptsAccessor(const Value& row); + static Value CurrentAttemptAccessor(const Value& row); + static Value StateAccessor(const Value& row); + static Value HasBeenCheckedAccessor(const Value& row); + static Value LastStateAccessor(const Value& row); + static Value LastHardStateAccessor(const Value& row); + static Value StateTypeAccessor(const Value& row); + static Value CheckTypeAccessor(const Value& row); + static Value AcknowledgedAccessor(const Value& row); + static Value AcknowledgementTypeAccessor(const Value& row); + static Value NoMoreNotificationsAccessor(const Value& row); + static Value LastTimeOkAccessor(const Value& row); + static Value LastTimeWarningAccessor(const Value& row); + static Value LastTimeCriticalAccessor(const Value& row); + static Value LastTimeUnknownAccessor(const Value& row); + static Value LastCheckAccessor(const Value& row); + static Value NextCheckAccessor(const Value& row); + static Value LastNotificationAccessor(const Value& row); + static Value NextNotificationAccessor(const Value& row); + static Value CurrentNotificationNumberAccessor(const Value& row); + static Value LastStateChangeAccessor(const Value& row); + static Value LastHardStateChangeAccessor(const Value& row); + static Value ScheduledDowntimeDepthAccessor(const Value& row); + static Value IsFlappingAccessor(const Value& row); + static Value ChecksEnabledAccessor(const Value& row); + static Value AcceptPassiveChecksAccessor(const Value& row); + static Value EventHandlerEnabledAccessor(const Value& row); + static Value NotificationsEnabledAccessor(const Value& row); + static Value ProcessPerformanceDataAccessor(const Value& row); + static Value ActiveChecksEnabledAccessor(const Value& row); + static Value FlapDetectionEnabledAccessor(const Value& row); + static Value StalenessAccessor(const Value& row); + static Value CheckIntervalAccessor(const Value& row); + static Value RetryIntervalAccessor(const Value& row); + static Value NotificationIntervalAccessor(const Value& row); + static Value LowFlapThresholdAccessor(const Value& row); + static Value HighFlapThresholdAccessor(const Value& row); + static Value LatencyAccessor(const Value& row); + static Value ExecutionTimeAccessor(const Value& row); + static Value PercentStateChangeAccessor(const Value& row); + static Value InCheckPeriodAccessor(const Value& row); + static Value InNotificationPeriodAccessor(const Value& row); + static Value ContactsAccessor(const Value& row); + static Value DowntimesAccessor(const Value& row); + static Value DowntimesWithInfoAccessor(const Value& row); + static Value CommentsAccessor(const Value& row); + static Value CommentsWithInfoAccessor(const Value& row); + static Value CommentsWithExtraInfoAccessor(const Value& row); + static Value CustomVariableNamesAccessor(const Value& row); + static Value CustomVariableValuesAccessor(const Value& row); + static Value CustomVariablesAccessor(const Value& row); + static Value GroupsAccessor(const Value& row); + static Value ContactGroupsAccessor(const Value& row); + static Value CheckSourceAccessor(const Value& row); + static Value IsReachableAccessor(const Value& row); + static Value CVIsJsonAccessor(const Value& row); + static Value OriginalAttributesAccessor(const Value& row); +}; + +} + +#endif /* SERVICESTABLE_H */ diff --git a/lib/livestatus/statehisttable.cpp b/lib/livestatus/statehisttable.cpp new file mode 100644 index 0000000..2d7e49b --- /dev/null +++ b/lib/livestatus/statehisttable.cpp @@ -0,0 +1,466 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/statehisttable.hpp" +#include "livestatus/livestatuslogutility.hpp" +#include "livestatus/hoststable.hpp" +#include "livestatus/servicestable.hpp" +#include "livestatus/contactstable.hpp" +#include "livestatus/commandstable.hpp" +#include "icinga/icingaapplication.hpp" +#include "icinga/cib.hpp" +#include "icinga/service.hpp" +#include "icinga/host.hpp" +#include "icinga/user.hpp" +#include "icinga/checkcommand.hpp" +#include "icinga/eventcommand.hpp" +#include "icinga/notificationcommand.hpp" +#include "base/convert.hpp" +#include "base/utility.hpp" +#include "base/logger.hpp" +#include "base/application.hpp" +#include "base/objectlock.hpp" +#include <boost/algorithm/string.hpp> +#include <boost/algorithm/string/replace.hpp> +#include <boost/algorithm/string/predicate.hpp> +#include <fstream> + +using namespace icinga; + +StateHistTable::StateHistTable(const String& compat_log_path, time_t from, time_t until) +{ + /* store attributes for FetchRows */ + m_TimeFrom = from; + m_TimeUntil = until; + m_CompatLogPath = compat_log_path; + + AddColumns(this); +} + +void StateHistTable::UpdateLogEntries(const Dictionary::Ptr& log_entry_attrs, int line_count, int lineno, const AddRowFunction& addRowFn) +{ + unsigned int time = log_entry_attrs->Get("time"); + String host_name = log_entry_attrs->Get("host_name"); + String service_description = log_entry_attrs->Get("service_description"); + unsigned long state = log_entry_attrs->Get("state"); + int log_type = log_entry_attrs->Get("log_type"); + String state_type = log_entry_attrs->Get("state_type"); //SOFT, HARD, STARTED, STOPPED, ... + String log_line = log_entry_attrs->Get("message"); /* use message from log table */ + + Checkable::Ptr checkable; + + if (service_description.IsEmpty()) + checkable = Host::GetByName(host_name); + else + checkable = Service::GetByNamePair(host_name, service_description); + + /* invalid log line for state history */ + if (!checkable) + return; + + Array::Ptr state_hist_service_states; + Dictionary::Ptr state_hist_bag; + unsigned long query_part = m_TimeUntil - m_TimeFrom; + + /* insert new service states array with values if not existing */ + if (m_CheckablesCache.find(checkable) == m_CheckablesCache.end()) { + + /* create new values */ + state_hist_service_states = new Array(); + state_hist_bag = new Dictionary(); + + Service::Ptr service = dynamic_pointer_cast<Service>(checkable); + Host::Ptr host; + + if (service) + host = service->GetHost(); + else + host = static_pointer_cast<Host>(checkable); + + state_hist_bag->Set("host_name", host->GetName()); + + if (service) + state_hist_bag->Set("service_description", service->GetShortName()); + + state_hist_bag->Set("state", state); + state_hist_bag->Set("in_downtime", 0); + state_hist_bag->Set("in_host_downtime", 0); + state_hist_bag->Set("in_notification_period", 1); // assume "always" + state_hist_bag->Set("is_flapping", 0); + state_hist_bag->Set("time", time); + state_hist_bag->Set("lineno", lineno); + state_hist_bag->Set("log_output", log_line); /* complete line */ + state_hist_bag->Set("from", time); /* starting at current timestamp */ + state_hist_bag->Set("until", time); /* will be updated later on state change */ + state_hist_bag->Set("query_part", query_part); /* required for _part calculations */ + + state_hist_service_states->Add(state_hist_bag); + + Log(LogDebug, "StateHistTable") + << "statehist: Adding new object '" << checkable->GetName() << "' to services cache."; + } else { + state_hist_service_states = m_CheckablesCache[checkable]; + state_hist_bag = state_hist_service_states->Get(state_hist_service_states->GetLength()-1); /* fetch latest state from history */ + + /* state duration */ + + /* determine service notifications notification_period and compare against current timestamp */ + bool in_notification_period = true; + String notification_period_name; + for (const Notification::Ptr& notification : checkable->GetNotifications()) { + TimePeriod::Ptr notification_period = notification->GetPeriod(); + + if (notification_period) { + if (notification_period->IsInside(static_cast<double>(time))) + in_notification_period = true; + else + in_notification_period = false; + + notification_period_name = notification_period->GetName(); // last one wins + } else + in_notification_period = true; // assume "always" + } + + /* check for state changes, flapping & downtime start/end */ + switch (log_type) { + case LogEntryTypeHostAlert: + case LogEntryTypeHostInitialState: + case LogEntryTypeHostCurrentState: + case LogEntryTypeServiceAlert: + case LogEntryTypeServiceInitialState: + case LogEntryTypeServiceCurrentState: + if (state != state_hist_bag->Get("state")) { + /* 1. seal old state_hist_bag */ + state_hist_bag->Set("until", time); /* add until record for duration calculation */ + + /* 2. add new state_hist_bag */ + Dictionary::Ptr state_hist_bag_new = new Dictionary(); + + state_hist_bag_new->Set("host_name", state_hist_bag->Get("host_name")); + state_hist_bag_new->Set("service_description", state_hist_bag->Get("service_description")); + state_hist_bag_new->Set("state", state); + state_hist_bag_new->Set("in_downtime", state_hist_bag->Get("in_downtime")); // keep value from previous state! + state_hist_bag_new->Set("in_host_downtime", state_hist_bag->Get("in_host_downtime")); // keep value from previous state! + state_hist_bag_new->Set("in_notification_period", (in_notification_period ? 1 : 0)); + state_hist_bag_new->Set("notification_period", notification_period_name); + state_hist_bag_new->Set("is_flapping", state_hist_bag->Get("is_flapping")); // keep value from previous state! + state_hist_bag_new->Set("time", time); + state_hist_bag_new->Set("lineno", lineno); + state_hist_bag_new->Set("log_output", log_line); /* complete line */ + state_hist_bag_new->Set("from", time); /* starting at current timestamp */ + state_hist_bag_new->Set("until", time + 1); /* will be updated later */ + state_hist_bag_new->Set("query_part", query_part); + + state_hist_service_states->Add(state_hist_bag_new); + + Log(LogDebug, "StateHistTable") + << "statehist: State change detected for object '" << checkable->GetName() << "' in '" << log_line << "'."; + } + break; + case LogEntryTypeHostFlapping: + case LogEntryTypeServiceFlapping: + if (state_type == "STARTED") + state_hist_bag->Set("is_flapping", 1); + else if (state_type == "STOPPED" || state_type == "DISABLED") + state_hist_bag->Set("is_flapping", 0); + break; + break; + case LogEntryTypeHostDowntimeAlert: + case LogEntryTypeServiceDowntimeAlert: + if (state_type == "STARTED") { + state_hist_bag->Set("in_downtime", 1); + if (log_type == LogEntryTypeHostDowntimeAlert) + state_hist_bag->Set("in_host_downtime", 1); + } + else if (state_type == "STOPPED" || state_type == "CANCELLED") { + state_hist_bag->Set("in_downtime", 0); + if (log_type == LogEntryTypeHostDowntimeAlert) + state_hist_bag->Set("in_host_downtime", 0); + } + break; + default: + //nothing to update + break; + } + + } + + m_CheckablesCache[checkable] = state_hist_service_states; + + /* TODO find a way to directly call addRowFn() - right now m_ServicesCache depends on historical lines ("already seen service") */ +} + +void StateHistTable::AddColumns(Table *table, const String& prefix, + const Column::ObjectAccessor& objectAccessor) +{ + table->AddColumn(prefix + "time", Column(&StateHistTable::TimeAccessor, objectAccessor)); + table->AddColumn(prefix + "lineno", Column(&StateHistTable::LinenoAccessor, objectAccessor)); + table->AddColumn(prefix + "from", Column(&StateHistTable::FromAccessor, objectAccessor)); + table->AddColumn(prefix + "until", Column(&StateHistTable::UntilAccessor, objectAccessor)); + table->AddColumn(prefix + "duration", Column(&StateHistTable::DurationAccessor, objectAccessor)); + table->AddColumn(prefix + "duration_part", Column(&StateHistTable::DurationPartAccessor, objectAccessor)); + table->AddColumn(prefix + "state", Column(&StateHistTable::StateAccessor, objectAccessor)); + table->AddColumn(prefix + "host_down", Column(&StateHistTable::HostDownAccessor, objectAccessor)); + table->AddColumn(prefix + "in_downtime", Column(&StateHistTable::InDowntimeAccessor, objectAccessor)); + table->AddColumn(prefix + "in_host_downtime", Column(&StateHistTable::InHostDowntimeAccessor, objectAccessor)); + table->AddColumn(prefix + "is_flapping", Column(&StateHistTable::IsFlappingAccessor, objectAccessor)); + table->AddColumn(prefix + "in_notification_period", Column(&StateHistTable::InNotificationPeriodAccessor, objectAccessor)); + table->AddColumn(prefix + "notification_period", Column(&StateHistTable::NotificationPeriodAccessor, objectAccessor)); + table->AddColumn(prefix + "debug_info", Column(&Table::EmptyStringAccessor, objectAccessor)); + table->AddColumn(prefix + "host_name", Column(&StateHistTable::HostNameAccessor, objectAccessor)); + table->AddColumn(prefix + "service_description", Column(&StateHistTable::ServiceDescriptionAccessor, objectAccessor)); + table->AddColumn(prefix + "log_output", Column(&StateHistTable::LogOutputAccessor, objectAccessor)); + table->AddColumn(prefix + "duration_ok", Column(&StateHistTable::DurationOkAccessor, objectAccessor)); + table->AddColumn(prefix + "duration_part_ok", Column(&StateHistTable::DurationPartOkAccessor, objectAccessor)); + table->AddColumn(prefix + "duration_warning", Column(&StateHistTable::DurationWarningAccessor, objectAccessor)); + table->AddColumn(prefix + "duration_part_warning", Column(&StateHistTable::DurationPartWarningAccessor, objectAccessor)); + table->AddColumn(prefix + "duration_critical", Column(&StateHistTable::DurationCriticalAccessor, objectAccessor)); + table->AddColumn(prefix + "duration_part_critical", Column(&StateHistTable::DurationPartCriticalAccessor, objectAccessor)); + table->AddColumn(prefix + "duration_unknown", Column(&StateHistTable::DurationUnknownAccessor, objectAccessor)); + table->AddColumn(prefix + "duration_part_unknown", Column(&StateHistTable::DurationPartUnknownAccessor, objectAccessor)); + table->AddColumn(prefix + "duration_unmonitored", Column(&StateHistTable::DurationUnmonitoredAccessor, objectAccessor)); + table->AddColumn(prefix + "duration_part_unmonitored", Column(&StateHistTable::DurationPartUnmonitoredAccessor, objectAccessor)); + + HostsTable::AddColumns(table, "current_host_", [objectAccessor](const Value& row, LivestatusGroupByType, const Object::Ptr&) -> Value { + return HostAccessor(row, objectAccessor); + }); + ServicesTable::AddColumns(table, "current_service_", [objectAccessor](const Value& row, LivestatusGroupByType, const Object::Ptr&) -> Value { + return ServiceAccessor(row, objectAccessor); + }); +} + +String StateHistTable::GetName() const +{ + return "log"; +} + +String StateHistTable::GetPrefix() const +{ + return "log"; +} + +void StateHistTable::FetchRows(const AddRowFunction& addRowFn) +{ + Log(LogDebug, "StateHistTable") + << "Pre-selecting log file from " << m_TimeFrom << " until " << m_TimeUntil; + + /* create log file index */ + LivestatusLogUtility::CreateLogIndex(m_CompatLogPath, m_LogFileIndex); + + /* generate log cache */ + LivestatusLogUtility::CreateLogCache(m_LogFileIndex, this, m_TimeFrom, m_TimeUntil, addRowFn); + + Checkable::Ptr checkable; + + for (const auto& kv : m_CheckablesCache) { + for (const Dictionary::Ptr& state_hist_bag : kv.second) { + /* pass a dictionary from state history array */ + if (!addRowFn(state_hist_bag, LivestatusGroupByNone, Empty)) + return; + } + } +} + +Object::Ptr StateHistTable::HostAccessor(const Value& row, const Column::ObjectAccessor&) +{ + String host_name = static_cast<Dictionary::Ptr>(row)->Get("host_name"); + + if (host_name.IsEmpty()) + return nullptr; + + return Host::GetByName(host_name); +} + +Object::Ptr StateHistTable::ServiceAccessor(const Value& row, const Column::ObjectAccessor&) +{ + String host_name = static_cast<Dictionary::Ptr>(row)->Get("host_name"); + String service_description = static_cast<Dictionary::Ptr>(row)->Get("service_description"); + + if (service_description.IsEmpty() || host_name.IsEmpty()) + return nullptr; + + return Service::GetByNamePair(host_name, service_description); +} + +Value StateHistTable::TimeAccessor(const Value& row) +{ + return static_cast<Dictionary::Ptr>(row)->Get("time"); +} + +Value StateHistTable::LinenoAccessor(const Value& row) +{ + return static_cast<Dictionary::Ptr>(row)->Get("lineno"); +} + +Value StateHistTable::FromAccessor(const Value& row) +{ + return static_cast<Dictionary::Ptr>(row)->Get("from"); +} + +Value StateHistTable::UntilAccessor(const Value& row) +{ + return static_cast<Dictionary::Ptr>(row)->Get("until"); +} + +Value StateHistTable::DurationAccessor(const Value& row) +{ + Dictionary::Ptr state_hist_bag = static_cast<Dictionary::Ptr>(row); + + return (state_hist_bag->Get("until") - state_hist_bag->Get("from")); +} + +Value StateHistTable::DurationPartAccessor(const Value& row) +{ + Dictionary::Ptr state_hist_bag = static_cast<Dictionary::Ptr>(row); + + return (state_hist_bag->Get("until") - state_hist_bag->Get("from")) / state_hist_bag->Get("query_part"); +} + +Value StateHistTable::StateAccessor(const Value& row) +{ + return static_cast<Dictionary::Ptr>(row)->Get("state"); +} + +Value StateHistTable::HostDownAccessor(const Value& row) +{ + return static_cast<Dictionary::Ptr>(row)->Get("host_down"); +} + +Value StateHistTable::InDowntimeAccessor(const Value& row) +{ + return static_cast<Dictionary::Ptr>(row)->Get("in_downtime"); +} + +Value StateHistTable::InHostDowntimeAccessor(const Value& row) +{ + return static_cast<Dictionary::Ptr>(row)->Get("in_host_downtime"); +} + +Value StateHistTable::IsFlappingAccessor(const Value& row) +{ + return static_cast<Dictionary::Ptr>(row)->Get("is_flapping"); +} + +Value StateHistTable::InNotificationPeriodAccessor(const Value& row) +{ + return static_cast<Dictionary::Ptr>(row)->Get("in_notification_period"); +} + +Value StateHistTable::NotificationPeriodAccessor(const Value& row) +{ + return static_cast<Dictionary::Ptr>(row)->Get("notification_period"); +} + +Value StateHistTable::HostNameAccessor(const Value& row) +{ + return static_cast<Dictionary::Ptr>(row)->Get("host_name"); +} + +Value StateHistTable::ServiceDescriptionAccessor(const Value& row) +{ + return static_cast<Dictionary::Ptr>(row)->Get("service_description"); +} + +Value StateHistTable::LogOutputAccessor(const Value& row) +{ + return static_cast<Dictionary::Ptr>(row)->Get("log_output"); +} + +Value StateHistTable::DurationOkAccessor(const Value& row) +{ + Dictionary::Ptr state_hist_bag = static_cast<Dictionary::Ptr>(row); + + if (state_hist_bag->Get("state") == ServiceOK) + return (state_hist_bag->Get("until") - state_hist_bag->Get("from")); + + return 0; +} + +Value StateHistTable::DurationPartOkAccessor(const Value& row) +{ + Dictionary::Ptr state_hist_bag = static_cast<Dictionary::Ptr>(row); + + if (state_hist_bag->Get("state") == ServiceOK) + return (state_hist_bag->Get("until") - state_hist_bag->Get("from")) / state_hist_bag->Get("query_part"); + + return 0; +} + +Value StateHistTable::DurationWarningAccessor(const Value& row) +{ + Dictionary::Ptr state_hist_bag = static_cast<Dictionary::Ptr>(row); + + if (state_hist_bag->Get("state") == ServiceWarning) + return (state_hist_bag->Get("until") - state_hist_bag->Get("from")); + + return 0; +} + +Value StateHistTable::DurationPartWarningAccessor(const Value& row) +{ + Dictionary::Ptr state_hist_bag = static_cast<Dictionary::Ptr>(row); + + if (state_hist_bag->Get("state") == ServiceWarning) + return (state_hist_bag->Get("until") - state_hist_bag->Get("from")) / state_hist_bag->Get("query_part"); + + return 0; +} + +Value StateHistTable::DurationCriticalAccessor(const Value& row) +{ + Dictionary::Ptr state_hist_bag = static_cast<Dictionary::Ptr>(row); + + if (state_hist_bag->Get("state") == ServiceCritical) + return (state_hist_bag->Get("until") - state_hist_bag->Get("from")); + + return 0; +} + +Value StateHistTable::DurationPartCriticalAccessor(const Value& row) +{ + Dictionary::Ptr state_hist_bag = static_cast<Dictionary::Ptr>(row); + + if (state_hist_bag->Get("state") == ServiceCritical) + return (state_hist_bag->Get("until") - state_hist_bag->Get("from")) / state_hist_bag->Get("query_part"); + + return 0; +} + +Value StateHistTable::DurationUnknownAccessor(const Value& row) +{ + Dictionary::Ptr state_hist_bag = static_cast<Dictionary::Ptr>(row); + + if (state_hist_bag->Get("state") == ServiceUnknown) + return (state_hist_bag->Get("until") - state_hist_bag->Get("from")); + + return 0; +} + +Value StateHistTable::DurationPartUnknownAccessor(const Value& row) +{ + Dictionary::Ptr state_hist_bag = static_cast<Dictionary::Ptr>(row); + + if (state_hist_bag->Get("state") == ServiceUnknown) + return (state_hist_bag->Get("until") - state_hist_bag->Get("from")) / state_hist_bag->Get("query_part"); + + return 0; +} + +Value StateHistTable::DurationUnmonitoredAccessor(const Value& row) +{ + Dictionary::Ptr state_hist_bag = static_cast<Dictionary::Ptr>(row); + + if (state_hist_bag->Get("state") == -1) + return (state_hist_bag->Get("until") - state_hist_bag->Get("from")); + + return 0; +} + +Value StateHistTable::DurationPartUnmonitoredAccessor(const Value& row) +{ + Dictionary::Ptr state_hist_bag = static_cast<Dictionary::Ptr>(row); + + if (state_hist_bag->Get("state") == -1) + return (state_hist_bag->Get("until") - state_hist_bag->Get("from")) / state_hist_bag->Get("query_part"); + + return 0; +} diff --git a/lib/livestatus/statehisttable.hpp b/lib/livestatus/statehisttable.hpp new file mode 100644 index 0000000..db00615 --- /dev/null +++ b/lib/livestatus/statehisttable.hpp @@ -0,0 +1,75 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef STATEHISTTABLE_H +#define STATEHISTTABLE_H + +#include "icinga/service.hpp" +#include "livestatus/historytable.hpp" + +using namespace icinga; + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +class StateHistTable final : public HistoryTable +{ +public: + DECLARE_PTR_TYPEDEFS(StateHistTable); + + StateHistTable(const String& compat_log_path, time_t from, time_t until); + + static void AddColumns(Table *table, const String& prefix = String(), + const Column::ObjectAccessor& objectAccessor = Column::ObjectAccessor()); + + String GetName() const override; + String GetPrefix() const override; + + void UpdateLogEntries(const Dictionary::Ptr& log_entry_attrs, int line_count, int lineno, const AddRowFunction& addRowFn) override; + +protected: + void FetchRows(const AddRowFunction& addRowFn) override; + + static Object::Ptr HostAccessor(const Value& row, const Column::ObjectAccessor& parentObjectAccessor); + static Object::Ptr ServiceAccessor(const Value& row, const Column::ObjectAccessor& parentObjectAccessor); + + static Value TimeAccessor(const Value& row); + static Value LinenoAccessor(const Value& row); + static Value FromAccessor(const Value& row); + static Value UntilAccessor(const Value& row); + static Value DurationAccessor(const Value& row); + static Value DurationPartAccessor(const Value& row); + static Value StateAccessor(const Value& row); + static Value HostDownAccessor(const Value& row); + static Value InDowntimeAccessor(const Value& row); + static Value InHostDowntimeAccessor(const Value& row); + static Value IsFlappingAccessor(const Value& row); + static Value InNotificationPeriodAccessor(const Value& row); + static Value NotificationPeriodAccessor(const Value& row); + static Value HostNameAccessor(const Value& row); + static Value ServiceDescriptionAccessor(const Value& row); + static Value LogOutputAccessor(const Value& row); + static Value DurationOkAccessor(const Value& row); + static Value DurationPartOkAccessor(const Value& row); + static Value DurationWarningAccessor(const Value& row); + static Value DurationPartWarningAccessor(const Value& row); + static Value DurationCriticalAccessor(const Value& row); + static Value DurationPartCriticalAccessor(const Value& row); + static Value DurationUnknownAccessor(const Value& row); + static Value DurationPartUnknownAccessor(const Value& row); + static Value DurationUnmonitoredAccessor(const Value& row); + static Value DurationPartUnmonitoredAccessor(const Value& row); + +private: + std::map<time_t, String> m_LogFileIndex; + std::map<Checkable::Ptr, Array::Ptr> m_CheckablesCache; + time_t m_TimeFrom; + time_t m_TimeUntil; + String m_CompatLogPath; +}; + +} + +#endif /* STATEHISTTABLE_H */ diff --git a/lib/livestatus/statustable.cpp b/lib/livestatus/statustable.cpp new file mode 100644 index 0000000..ae0a736 --- /dev/null +++ b/lib/livestatus/statustable.cpp @@ -0,0 +1,269 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/statustable.hpp" +#include "livestatus/livestatuslistener.hpp" +#include "icinga/icingaapplication.hpp" +#include "icinga/cib.hpp" +#include "icinga/host.hpp" +#include "icinga/service.hpp" +#include "base/configtype.hpp" +#include "base/utility.hpp" +#include "base/application.hpp" + +using namespace icinga; + +StatusTable::StatusTable() +{ + AddColumns(this); +} + +void StatusTable::AddColumns(Table *table, const String& prefix, + const Column::ObjectAccessor& objectAccessor) +{ + table->AddColumn(prefix + "neb_callbacks", Column(&Table::ZeroAccessor, objectAccessor)); + table->AddColumn(prefix + "neb_callbacks_rate", Column(&Table::ZeroAccessor, objectAccessor)); + + table->AddColumn(prefix + "requests", Column(&Table::ZeroAccessor, objectAccessor)); + table->AddColumn(prefix + "requests_rate", Column(&Table::ZeroAccessor, objectAccessor)); + + table->AddColumn(prefix + "connections", Column(&StatusTable::ConnectionsAccessor, objectAccessor)); + table->AddColumn(prefix + "connections_rate", Column(&StatusTable::ConnectionsRateAccessor, objectAccessor)); + + table->AddColumn(prefix + "service_checks", Column(&StatusTable::ServiceChecksAccessor, objectAccessor)); + table->AddColumn(prefix + "service_checks_rate", Column(&StatusTable::ServiceChecksRateAccessor, objectAccessor)); + + table->AddColumn(prefix + "host_checks", Column(&StatusTable::HostChecksAccessor, objectAccessor)); + table->AddColumn(prefix + "host_checks_rate", Column(&StatusTable::HostChecksRateAccessor, objectAccessor)); + + table->AddColumn(prefix + "forks", Column(&Table::ZeroAccessor, objectAccessor)); + table->AddColumn(prefix + "forks_rate", Column(&Table::ZeroAccessor, objectAccessor)); + + table->AddColumn(prefix + "log_messages", Column(&Table::ZeroAccessor, objectAccessor)); + table->AddColumn(prefix + "log_messages_rate", Column(&Table::ZeroAccessor, objectAccessor)); + + table->AddColumn(prefix + "external_commands", Column(&StatusTable::ExternalCommandsAccessor, objectAccessor)); + table->AddColumn(prefix + "external_commands_rate", Column(&StatusTable::ExternalCommandsRateAccessor, objectAccessor)); + + table->AddColumn(prefix + "livechecks", Column(&Table::ZeroAccessor, objectAccessor)); + table->AddColumn(prefix + "livechecks_rate", Column(&Table::ZeroAccessor, objectAccessor)); + + table->AddColumn(prefix + "livecheck_overflows", Column(&Table::ZeroAccessor, objectAccessor)); + table->AddColumn(prefix + "livecheck_overflows_rate", Column(&Table::ZeroAccessor, objectAccessor)); + + table->AddColumn(prefix + "nagios_pid", Column(&StatusTable::NagiosPidAccessor, objectAccessor)); + table->AddColumn(prefix + "enable_notifications", Column(&StatusTable::EnableNotificationsAccessor, objectAccessor)); + table->AddColumn(prefix + "execute_service_checks", Column(&StatusTable::ExecuteServiceChecksAccessor, objectAccessor)); + table->AddColumn(prefix + "accept_passive_service_checks", Column(&Table::OneAccessor, objectAccessor)); + table->AddColumn(prefix + "execute_host_checks", Column(&StatusTable::ExecuteHostChecksAccessor, objectAccessor)); + table->AddColumn(prefix + "accept_passive_host_checks", Column(&Table::OneAccessor, objectAccessor)); + table->AddColumn(prefix + "enable_event_handlers", Column(&StatusTable::EnableEventHandlersAccessor, objectAccessor)); + table->AddColumn(prefix + "obsess_over_services", Column(&Table::ZeroAccessor, objectAccessor)); + table->AddColumn(prefix + "obsess_over_hosts", Column(&Table::ZeroAccessor, objectAccessor)); + table->AddColumn(prefix + "check_service_freshness", Column(&Table::OneAccessor, objectAccessor)); + table->AddColumn(prefix + "check_host_freshness", Column(&Table::OneAccessor, objectAccessor)); + table->AddColumn(prefix + "enable_flap_detection", Column(&StatusTable::EnableFlapDetectionAccessor, objectAccessor)); + table->AddColumn(prefix + "process_performance_data", Column(&StatusTable::ProcessPerformanceDataAccessor, objectAccessor)); + table->AddColumn(prefix + "check_external_commands", Column(&Table::OneAccessor, objectAccessor)); + table->AddColumn(prefix + "program_start", Column(&StatusTable::ProgramStartAccessor, objectAccessor)); + table->AddColumn(prefix + "last_command_check", Column(&Table::ZeroAccessor, objectAccessor)); + table->AddColumn(prefix + "last_log_rotation", Column(&Table::ZeroAccessor, objectAccessor)); + table->AddColumn(prefix + "interval_length", Column(&StatusTable::IntervalLengthAccessor, objectAccessor)); + table->AddColumn(prefix + "num_hosts", Column(&StatusTable::NumHostsAccessor, objectAccessor)); + table->AddColumn(prefix + "num_services", Column(&StatusTable::NumServicesAccessor, objectAccessor)); + table->AddColumn(prefix + "program_version", Column(&StatusTable::ProgramVersionAccessor, objectAccessor)); + table->AddColumn(prefix + "external_command_buffer_slots", Column(&Table::ZeroAccessor, objectAccessor)); + table->AddColumn(prefix + "external_command_buffer_usage", Column(&Table::ZeroAccessor, objectAccessor)); + table->AddColumn(prefix + "external_command_buffer_max", Column(&Table::ZeroAccessor, objectAccessor)); + table->AddColumn(prefix + "cached_log_messages", Column(&Table::ZeroAccessor, objectAccessor)); + table->AddColumn(prefix + "livestatus_version", Column(&StatusTable::LivestatusVersionAccessor, objectAccessor)); + table->AddColumn(prefix + "livestatus_active_connections", Column(&Table::ZeroAccessor, objectAccessor)); + table->AddColumn(prefix + "livestatus_queued_connections", Column(&Table::ZeroAccessor, objectAccessor)); + table->AddColumn(prefix + "livestatus_threads", Column(&Table::ZeroAccessor, objectAccessor)); + + table->AddColumn(prefix + "custom_variable_names", Column(&StatusTable::CustomVariableNamesAccessor, objectAccessor)); + table->AddColumn(prefix + "custom_variable_values", Column(&StatusTable::CustomVariableValuesAccessor, objectAccessor)); + table->AddColumn(prefix + "custom_variables", Column(&StatusTable::CustomVariablesAccessor, objectAccessor)); +} + +String StatusTable::GetName() const +{ + return "status"; +} + +String StatusTable::GetPrefix() const +{ + return "status"; +} + +void StatusTable::FetchRows(const AddRowFunction& addRowFn) +{ + Object::Ptr obj = new Object(); + + /* Return a fake row. */ + addRowFn(obj, LivestatusGroupByNone, Empty); +} + +Value StatusTable::ConnectionsAccessor(const Value&) +{ + return LivestatusListener::GetConnections(); +} + +Value StatusTable::ConnectionsRateAccessor(const Value&) +{ + return (LivestatusListener::GetConnections() / (Utility::GetTime() - Application::GetStartTime())); +} + +Value StatusTable::HostChecksAccessor(const Value&) +{ + auto timespan = static_cast<long>(Utility::GetTime() - Application::GetStartTime()); + return CIB::GetActiveHostChecksStatistics(timespan); +} + +Value StatusTable::HostChecksRateAccessor(const Value&) +{ + auto timespan = static_cast<long>(Utility::GetTime() - Application::GetStartTime()); + return (CIB::GetActiveHostChecksStatistics(timespan) / (Utility::GetTime() - Application::GetStartTime())); +} + +Value StatusTable::ServiceChecksAccessor(const Value&) +{ + auto timespan = static_cast<long>(Utility::GetTime() - Application::GetStartTime()); + return CIB::GetActiveServiceChecksStatistics(timespan); +} + +Value StatusTable::ServiceChecksRateAccessor(const Value&) +{ + auto timespan = static_cast<long>(Utility::GetTime() - Application::GetStartTime()); + return (CIB::GetActiveServiceChecksStatistics(timespan) / (Utility::GetTime() - Application::GetStartTime())); +} + +Value StatusTable::ExternalCommandsAccessor(const Value&) +{ + return LivestatusQuery::GetExternalCommands(); +} + +Value StatusTable::ExternalCommandsRateAccessor(const Value&) +{ + return (LivestatusQuery::GetExternalCommands() / (Utility::GetTime() - Application::GetStartTime())); +} + +Value StatusTable::NagiosPidAccessor(const Value&) +{ + return Utility::GetPid(); +} + +Value StatusTable::EnableNotificationsAccessor(const Value&) +{ + return (IcingaApplication::GetInstance()->GetEnableNotifications() ? 1 : 0); +} + +Value StatusTable::ExecuteServiceChecksAccessor(const Value&) +{ + return (IcingaApplication::GetInstance()->GetEnableServiceChecks() ? 1 : 0); +} + +Value StatusTable::ExecuteHostChecksAccessor(const Value&) +{ + return (IcingaApplication::GetInstance()->GetEnableHostChecks() ? 1 : 0); +} + +Value StatusTable::EnableEventHandlersAccessor(const Value&) +{ + return (IcingaApplication::GetInstance()->GetEnableEventHandlers() ? 1 : 0); +} + +Value StatusTable::EnableFlapDetectionAccessor(const Value&) +{ + return (IcingaApplication::GetInstance()->GetEnableFlapping() ? 1 : 0); +} + +Value StatusTable::ProcessPerformanceDataAccessor(const Value&) +{ + return (IcingaApplication::GetInstance()->GetEnablePerfdata() ? 1 : 0); +} + +Value StatusTable::ProgramStartAccessor(const Value&) +{ + return static_cast<long>(Application::GetStartTime()); +} + +Value StatusTable::IntervalLengthAccessor(const Value&) +{ + return LIVESTATUS_INTERVAL_LENGTH; +} + +Value StatusTable::NumHostsAccessor(const Value&) +{ + return ConfigType::Get<Host>()->GetObjectCount(); +} + +Value StatusTable::NumServicesAccessor(const Value&) +{ + return ConfigType::Get<Service>()->GetObjectCount(); +} + +Value StatusTable::ProgramVersionAccessor(const Value&) +{ + return Application::GetAppVersion() + "-icinga2"; +} + +Value StatusTable::LivestatusVersionAccessor(const Value&) +{ + return Application::GetAppVersion(); +} + +Value StatusTable::LivestatusActiveConnectionsAccessor(const Value&) +{ + return LivestatusListener::GetClientsConnected(); +} + +Value StatusTable::CustomVariableNamesAccessor(const Value&) +{ + Dictionary::Ptr vars = IcingaApplication::GetInstance()->GetVars(); + + ArrayData result; + + if (vars) { + ObjectLock olock(vars); + for (const auto& kv : vars) { + result.push_back(kv.first); + } + } + + return new Array(std::move(result)); +} + +Value StatusTable::CustomVariableValuesAccessor(const Value&) +{ + Dictionary::Ptr vars = IcingaApplication::GetInstance()->GetVars(); + + ArrayData result; + + if (vars) { + ObjectLock olock(vars); + for (const auto& kv : vars) { + result.push_back(kv.second); + } + } + + return new Array(std::move(result)); +} + +Value StatusTable::CustomVariablesAccessor(const Value&) +{ + Dictionary::Ptr vars = IcingaApplication::GetInstance()->GetVars(); + + ArrayData result; + + if (vars) { + ObjectLock olock(vars); + for (const auto& kv : vars) { + result.push_back(new Array({ + kv.first, + kv.second + })); + } + } + + return new Array(std::move(result)); +} diff --git a/lib/livestatus/statustable.hpp b/lib/livestatus/statustable.hpp new file mode 100644 index 0000000..2fba249 --- /dev/null +++ b/lib/livestatus/statustable.hpp @@ -0,0 +1,61 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef STATUSTABLE_H +#define STATUSTABLE_H + +#include "livestatus/table.hpp" + +using namespace icinga; + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +class StatusTable final : public Table +{ +public: + DECLARE_PTR_TYPEDEFS(StatusTable); + + StatusTable(); + + static void AddColumns(Table *table, const String& prefix = String(), + const Column::ObjectAccessor& objectAccessor = Column::ObjectAccessor()); + + String GetName() const override; + String GetPrefix() const override; + +protected: + void FetchRows(const AddRowFunction& addRowFn) override; + + static Value ConnectionsAccessor(const Value& row); + static Value ConnectionsRateAccessor(const Value& row); + static Value ServiceChecksAccessor(const Value& row); + static Value ServiceChecksRateAccessor(const Value& row); + static Value HostChecksAccessor(const Value& row); + static Value HostChecksRateAccessor(const Value& row); + static Value ExternalCommandsAccessor(const Value& row); + static Value ExternalCommandsRateAccessor(const Value& row); + static Value NagiosPidAccessor(const Value& row); + static Value EnableNotificationsAccessor(const Value& row); + static Value ExecuteServiceChecksAccessor(const Value& row); + static Value ExecuteHostChecksAccessor(const Value& row); + static Value EnableEventHandlersAccessor(const Value& row); + static Value EnableFlapDetectionAccessor(const Value& row); + static Value ProcessPerformanceDataAccessor(const Value& row); + static Value ProgramStartAccessor(const Value& row); + static Value IntervalLengthAccessor(const Value& row); + static Value NumHostsAccessor(const Value& row); + static Value NumServicesAccessor(const Value& row); + static Value ProgramVersionAccessor(const Value& row); + static Value LivestatusVersionAccessor(const Value& row); + static Value LivestatusActiveConnectionsAccessor(const Value& row); + static Value CustomVariableNamesAccessor(const Value& row); + static Value CustomVariableValuesAccessor(const Value& row); + static Value CustomVariablesAccessor(const Value& row); +}; + +} + +#endif /* STATUSTABLE_H */ diff --git a/lib/livestatus/stdaggregator.cpp b/lib/livestatus/stdaggregator.cpp new file mode 100644 index 0000000..99c3a8e --- /dev/null +++ b/lib/livestatus/stdaggregator.cpp @@ -0,0 +1,40 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/stdaggregator.hpp" +#include <math.h> + +using namespace icinga; + +StdAggregator::StdAggregator(String attr) + : m_StdAttr(std::move(attr)) +{ } + +StdAggregatorState *StdAggregator::EnsureState(AggregatorState **state) +{ + if (!*state) + *state = new StdAggregatorState(); + + return static_cast<StdAggregatorState *>(*state); +} + +void StdAggregator::Apply(const Table::Ptr& table, const Value& row, AggregatorState **state) +{ + Column column = table->GetColumn(m_StdAttr); + + Value value = column.ExtractValue(row); + + StdAggregatorState *pstate = EnsureState(state); + + pstate->StdSum += value; + pstate->StdQSum += pow(value, 2); + pstate->StdCount++; +} + +double StdAggregator::GetResultAndFreeState(AggregatorState *state) const +{ + StdAggregatorState *pstate = EnsureState(&state); + double result = sqrt((pstate->StdQSum - (1 / pstate->StdCount) * pow(pstate->StdSum, 2)) / (pstate->StdCount - 1)); + delete pstate; + + return result; +} diff --git a/lib/livestatus/stdaggregator.hpp b/lib/livestatus/stdaggregator.hpp new file mode 100644 index 0000000..3680fe7 --- /dev/null +++ b/lib/livestatus/stdaggregator.hpp @@ -0,0 +1,43 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef STDAGGREGATOR_H +#define STDAGGREGATOR_H + +#include "livestatus/table.hpp" +#include "livestatus/aggregator.hpp" + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +struct StdAggregatorState final : public AggregatorState +{ + double StdSum{0}; + double StdQSum{0}; + double StdCount{0}; +}; + +/** + * @ingroup livestatus + */ +class StdAggregator final : public Aggregator +{ +public: + DECLARE_PTR_TYPEDEFS(StdAggregator); + + StdAggregator(String attr); + + void Apply(const Table::Ptr& table, const Value& row, AggregatorState **state) override; + double GetResultAndFreeState(AggregatorState *state) const override; + +private: + String m_StdAttr; + + static StdAggregatorState *EnsureState(AggregatorState **state); +}; + +} + +#endif /* STDAGGREGATOR_H */ diff --git a/lib/livestatus/sumaggregator.cpp b/lib/livestatus/sumaggregator.cpp new file mode 100644 index 0000000..fc4b62e --- /dev/null +++ b/lib/livestatus/sumaggregator.cpp @@ -0,0 +1,37 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/sumaggregator.hpp" + +using namespace icinga; + +SumAggregator::SumAggregator(String attr) + : m_SumAttr(std::move(attr)) +{ } + +SumAggregatorState *SumAggregator::EnsureState(AggregatorState **state) +{ + if (!*state) + *state = new SumAggregatorState(); + + return static_cast<SumAggregatorState *>(*state); +} + +void SumAggregator::Apply(const Table::Ptr& table, const Value& row, AggregatorState **state) +{ + Column column = table->GetColumn(m_SumAttr); + + Value value = column.ExtractValue(row); + + SumAggregatorState *pstate = EnsureState(state); + + pstate->Sum += value; +} + +double SumAggregator::GetResultAndFreeState(AggregatorState *state) const +{ + SumAggregatorState *pstate = EnsureState(&state); + double result = pstate->Sum; + delete pstate; + + return result; +} diff --git a/lib/livestatus/sumaggregator.hpp b/lib/livestatus/sumaggregator.hpp new file mode 100644 index 0000000..23f22fb --- /dev/null +++ b/lib/livestatus/sumaggregator.hpp @@ -0,0 +1,41 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef SUMAGGREGATOR_H +#define SUMAGGREGATOR_H + +#include "livestatus/table.hpp" +#include "livestatus/aggregator.hpp" + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +struct SumAggregatorState final : public AggregatorState +{ + double Sum{0}; +}; + +/** + * @ingroup livestatus + */ +class SumAggregator final : public Aggregator +{ +public: + DECLARE_PTR_TYPEDEFS(SumAggregator); + + SumAggregator(String attr); + + void Apply(const Table::Ptr& table, const Value& row, AggregatorState **state) override; + double GetResultAndFreeState(AggregatorState *state) const override; + +private: + String m_SumAttr; + + static SumAggregatorState *EnsureState(AggregatorState **state); +}; + +} + +#endif /* SUMAGGREGATOR_H */ diff --git a/lib/livestatus/table.cpp b/lib/livestatus/table.cpp new file mode 100644 index 0000000..8e5d85a --- /dev/null +++ b/lib/livestatus/table.cpp @@ -0,0 +1,165 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/table.hpp" +#include "livestatus/statustable.hpp" +#include "livestatus/contactgroupstable.hpp" +#include "livestatus/contactstable.hpp" +#include "livestatus/hostgroupstable.hpp" +#include "livestatus/hoststable.hpp" +#include "livestatus/servicegroupstable.hpp" +#include "livestatus/servicestable.hpp" +#include "livestatus/commandstable.hpp" +#include "livestatus/commentstable.hpp" +#include "livestatus/downtimestable.hpp" +#include "livestatus/endpointstable.hpp" +#include "livestatus/zonestable.hpp" +#include "livestatus/timeperiodstable.hpp" +#include "livestatus/logtable.hpp" +#include "livestatus/statehisttable.hpp" +#include "livestatus/filter.hpp" +#include "base/array.hpp" +#include "base/dictionary.hpp" +#include <boost/algorithm/string/case_conv.hpp> + +using namespace icinga; + +Table::Table(LivestatusGroupByType type) + : m_GroupByType(type), m_GroupByObject(Empty) +{ } + +Table::Ptr Table::GetByName(const String& name, const String& compat_log_path, const unsigned long& from, const unsigned long& until) +{ + if (name == "status") + return new StatusTable(); + else if (name == "contactgroups") + return new ContactGroupsTable(); + else if (name == "contacts") + return new ContactsTable(); + else if (name == "hostgroups") + return new HostGroupsTable(); + else if (name == "hosts") + return new HostsTable(); + else if (name == "hostsbygroup") + return new HostsTable(LivestatusGroupByHostGroup); + else if (name == "servicegroups") + return new ServiceGroupsTable(); + else if (name == "services") + return new ServicesTable(); + else if (name == "servicesbygroup") + return new ServicesTable(LivestatusGroupByServiceGroup); + else if (name == "servicesbyhostgroup") + return new ServicesTable(LivestatusGroupByHostGroup); + else if (name == "commands") + return new CommandsTable(); + else if (name == "comments") + return new CommentsTable(); + else if (name == "downtimes") + return new DowntimesTable(); + else if (name == "timeperiods") + return new TimePeriodsTable(); + else if (name == "log") + return new LogTable(compat_log_path, from, until); + else if (name == "statehist") + return new StateHistTable(compat_log_path, from, until); + else if (name == "endpoints") + return new EndpointsTable(); + else if (name == "zones") + return new ZonesTable(); + + return nullptr; +} + +void Table::AddColumn(const String& name, const Column& column) +{ + std::pair<String, Column> item = std::make_pair(name, column); + + auto ret = m_Columns.insert(item); + + if (!ret.second) + ret.first->second = column; +} + +Column Table::GetColumn(const String& name) const +{ + String dname = name; + String prefix = GetPrefix() + "_"; + + if (dname.Find(prefix) == 0) + dname = dname.SubStr(prefix.GetLength()); + + auto it = m_Columns.find(dname); + + if (it == m_Columns.end()) + BOOST_THROW_EXCEPTION(std::invalid_argument("Column '" + dname + "' does not exist in table '" + GetName() + "'.")); + + return it->second; +} + +std::vector<String> Table::GetColumnNames() const +{ + std::vector<String> names; + + for (const auto& kv : m_Columns) { + names.push_back(kv.first); + } + + return names; +} + +std::vector<LivestatusRowValue> Table::FilterRows(const Filter::Ptr& filter, int limit) +{ + std::vector<LivestatusRowValue> rs; + + FetchRows([this, filter, limit, &rs](const Value& row, LivestatusGroupByType groupByType, const Object::Ptr& groupByObject) { + return FilteredAddRow(rs, filter, limit, row, groupByType, groupByObject); + }); + + return rs; +} + +bool Table::FilteredAddRow(std::vector<LivestatusRowValue>& rs, const Filter::Ptr& filter, int limit, const Value& row, LivestatusGroupByType groupByType, const Object::Ptr& groupByObject) +{ + if (limit != -1 && static_cast<int>(rs.size()) == limit) + return false; + + if (!filter || filter->Apply(this, row)) { + LivestatusRowValue rval; + rval.Row = row; + rval.GroupByType = groupByType; + rval.GroupByObject = groupByObject; + + rs.emplace_back(std::move(rval)); + } + + return true; +} + +Value Table::ZeroAccessor(const Value&) +{ + return 0; +} + +Value Table::OneAccessor(const Value&) +{ + return 1; +} + +Value Table::EmptyStringAccessor(const Value&) +{ + return ""; +} + +Value Table::EmptyArrayAccessor(const Value&) +{ + return new Array(); +} + +Value Table::EmptyDictionaryAccessor(const Value&) +{ + return new Dictionary(); +} + +LivestatusGroupByType Table::GetGroupByType() const +{ + return m_GroupByType; +} diff --git a/lib/livestatus/table.hpp b/lib/livestatus/table.hpp new file mode 100644 index 0000000..fa3fc2a --- /dev/null +++ b/lib/livestatus/table.hpp @@ -0,0 +1,73 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef TABLE_H +#define TABLE_H + +#include "livestatus/column.hpp" +#include "base/object.hpp" +#include "base/dictionary.hpp" +#include "base/array.hpp" +#include <vector> + +namespace icinga +{ + +// Well, don't ask. +#define LIVESTATUS_INTERVAL_LENGTH 60.0 + +struct LivestatusRowValue { + Value Row; + LivestatusGroupByType GroupByType; + Value GroupByObject; +}; + +typedef std::function<bool (const Value&, LivestatusGroupByType, const Object::Ptr&)> AddRowFunction; + +class Filter; + +/** + * @ingroup livestatus + */ +class Table : public Object +{ +public: + DECLARE_PTR_TYPEDEFS(Table); + + static Table::Ptr GetByName(const String& name, const String& compat_log_path = "", const unsigned long& from = 0, const unsigned long& until = 0); + + virtual String GetName() const = 0; + virtual String GetPrefix() const = 0; + + std::vector<LivestatusRowValue> FilterRows(const intrusive_ptr<Filter>& filter, int limit = -1); + + void AddColumn(const String& name, const Column& column); + Column GetColumn(const String& name) const; + std::vector<String> GetColumnNames() const; + + LivestatusGroupByType GetGroupByType() const; + +protected: + Table(LivestatusGroupByType type = LivestatusGroupByNone); + + virtual void FetchRows(const AddRowFunction& addRowFn) = 0; + + static Value ZeroAccessor(const Value&); + static Value OneAccessor(const Value&); + static Value EmptyStringAccessor(const Value&); + static Value EmptyArrayAccessor(const Value&); + static Value EmptyDictionaryAccessor(const Value&); + + LivestatusGroupByType m_GroupByType; + Value m_GroupByObject; + +private: + std::map<String, Column> m_Columns; + + bool FilteredAddRow(std::vector<LivestatusRowValue>& rs, const intrusive_ptr<Filter>& filter, int limit, const Value& row, LivestatusGroupByType groupByType, const Object::Ptr& groupByObject); +}; + +} + +#endif /* TABLE_H */ + +#include "livestatus/filter.hpp" diff --git a/lib/livestatus/timeperiodstable.cpp b/lib/livestatus/timeperiodstable.cpp new file mode 100644 index 0000000..5797d93 --- /dev/null +++ b/lib/livestatus/timeperiodstable.cpp @@ -0,0 +1,58 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/timeperiodstable.hpp" +#include "icinga/icingaapplication.hpp" +#include "icinga/timeperiod.hpp" +#include "base/configtype.hpp" +#include "base/objectlock.hpp" +#include "base/convert.hpp" +#include "base/utility.hpp" +#include <boost/algorithm/string/replace.hpp> + +using namespace icinga; + +TimePeriodsTable::TimePeriodsTable() +{ + AddColumns(this); +} + +void TimePeriodsTable::AddColumns(Table *table, const String& prefix, + const Column::ObjectAccessor& objectAccessor) +{ + table->AddColumn(prefix + "name", Column(&TimePeriodsTable::NameAccessor, objectAccessor)); + table->AddColumn(prefix + "alias", Column(&TimePeriodsTable::AliasAccessor, objectAccessor)); + table->AddColumn(prefix + "in", Column(&TimePeriodsTable::InAccessor, objectAccessor)); +} + +String TimePeriodsTable::GetName() const +{ + return "timeperiod"; +} + +String TimePeriodsTable::GetPrefix() const +{ + return "timeperiod"; +} + +void TimePeriodsTable::FetchRows(const AddRowFunction& addRowFn) +{ + for (const TimePeriod::Ptr& tp : ConfigType::GetObjectsByType<TimePeriod>()) { + if (!addRowFn(tp, LivestatusGroupByNone, Empty)) + return; + } +} + +Value TimePeriodsTable::NameAccessor(const Value& row) +{ + return static_cast<TimePeriod::Ptr>(row)->GetName(); +} + +Value TimePeriodsTable::AliasAccessor(const Value& row) +{ + return static_cast<TimePeriod::Ptr>(row)->GetDisplayName(); +} + +Value TimePeriodsTable::InAccessor(const Value& row) +{ + return (static_cast<TimePeriod::Ptr>(row)->IsInside(Utility::GetTime()) ? 1 : 0); +} diff --git a/lib/livestatus/timeperiodstable.hpp b/lib/livestatus/timeperiodstable.hpp new file mode 100644 index 0000000..31cef93 --- /dev/null +++ b/lib/livestatus/timeperiodstable.hpp @@ -0,0 +1,39 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef TIMEPERIODSTABLE_H +#define TIMEPERIODSTABLE_H + +#include "livestatus/table.hpp" + +using namespace icinga; + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +class TimePeriodsTable final : public Table +{ +public: + DECLARE_PTR_TYPEDEFS(TimePeriodsTable); + + TimePeriodsTable(); + + static void AddColumns(Table *table, const String& prefix = String(), + const Column::ObjectAccessor& objectAccessor = Column::ObjectAccessor()); + + String GetName() const override; + String GetPrefix() const override; + +protected: + void FetchRows(const AddRowFunction& addRowFn) override; + + static Value NameAccessor(const Value& row); + static Value AliasAccessor(const Value& row); + static Value InAccessor(const Value& row); +}; + +} + +#endif /* TIMEPERIODSTABLE_H */ diff --git a/lib/livestatus/zonestable.cpp b/lib/livestatus/zonestable.cpp new file mode 100644 index 0000000..d86cc72 --- /dev/null +++ b/lib/livestatus/zonestable.cpp @@ -0,0 +1,92 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "livestatus/zonestable.hpp" +#include "remote/zone.hpp" +#include "base/configtype.hpp" + +using namespace icinga; + +ZonesTable::ZonesTable() +{ + AddColumns(this); +} + +void ZonesTable::AddColumns(Table *table, const String& prefix, + const Column::ObjectAccessor& objectAccessor) +{ + table->AddColumn(prefix + "name", Column(&ZonesTable::NameAccessor, objectAccessor)); + table->AddColumn(prefix + "parent", Column(&ZonesTable::ParentAccessor, objectAccessor)); + table->AddColumn(prefix + "endpoints", Column(&ZonesTable::EndpointsAccessor, objectAccessor)); + table->AddColumn(prefix + "global", Column(&ZonesTable::GlobalAccessor, objectAccessor)); +} + +String ZonesTable::GetName() const +{ + return "zones"; +} + +String ZonesTable::GetPrefix() const +{ + return "zone"; +} + +void ZonesTable::FetchRows(const AddRowFunction& addRowFn) +{ + for (const Zone::Ptr& zone : ConfigType::GetObjectsByType<Zone>()) { + if (!addRowFn(zone, LivestatusGroupByNone, Empty)) + return; + } +} + +Value ZonesTable::NameAccessor(const Value& row) +{ + Zone::Ptr zone = static_cast<Zone::Ptr>(row); + + if (!zone) + return Empty; + + return zone->GetName(); +} + +Value ZonesTable::ParentAccessor(const Value& row) +{ + Zone::Ptr zone = static_cast<Zone::Ptr>(row); + + if (!zone) + return Empty; + + Zone::Ptr parent_zone = zone->GetParent(); + + if (!parent_zone) + return Empty; + + return parent_zone->GetName(); +} + +Value ZonesTable::EndpointsAccessor(const Value& row) +{ + Zone::Ptr zone = static_cast<Zone::Ptr>(row); + + if (!zone) + return Empty; + + std::set<Endpoint::Ptr> endpoints = zone->GetEndpoints(); + + ArrayData result; + + for (const Endpoint::Ptr& endpoint : endpoints) { + result.push_back(endpoint->GetName()); + } + + return new Array(std::move(result)); +} + +Value ZonesTable::GlobalAccessor(const Value& row) +{ + Zone::Ptr zone = static_cast<Zone::Ptr>(row); + + if (!zone) + return Empty; + + return zone->GetGlobal() ? 1 : 0; +} diff --git a/lib/livestatus/zonestable.hpp b/lib/livestatus/zonestable.hpp new file mode 100644 index 0000000..fb03488 --- /dev/null +++ b/lib/livestatus/zonestable.hpp @@ -0,0 +1,40 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef ZONESTABLE_H +#define ZONESTABLE_H + +#include "livestatus/table.hpp" + +using namespace icinga; + +namespace icinga +{ + +/** + * @ingroup livestatus + */ +class ZonesTable final : public Table +{ +public: + DECLARE_PTR_TYPEDEFS(ZonesTable); + + ZonesTable(); + + static void AddColumns(Table *table, const String& prefix = String(), + const Column::ObjectAccessor& objectAccessor = Column::ObjectAccessor()); + + String GetName() const override; + String GetPrefix() const override; + +protected: + void FetchRows(const AddRowFunction& addRowFn) override; + + static Value NameAccessor(const Value& row); + static Value ParentAccessor(const Value& row); + static Value EndpointsAccessor(const Value& row); + static Value GlobalAccessor(const Value& row); +}; + +} + +#endif /* ZONESTABLE_H */ |