]> granicus.if.org Git - icinga2/blobdiff - lib/livestatus/livestatusquery.cpp
Move CompatUtility::GetCheckableInCheckPeriod() into Livestatus feature
[icinga2] / lib / livestatus / livestatusquery.cpp
index c216da6bc3fcb220a39c1fc517efec6712df644c..a8e7577aa21813933d77ba4ca7656347d6c78030 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  * Icinga 2                                                                   *
- * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org)    *
+ * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/)  *
  *                                                                            *
  * This program is free software; you can redistribute it and/or              *
  * modify it under the terms of the GNU General Public License                *
@@ -31,7 +31,6 @@
 #include "livestatus/orfilter.hpp"
 #include "livestatus/andfilter.hpp"
 #include "icinga/externalcommandprocessor.hpp"
-#include "config/configcompiler.hpp"
 #include "base/debug.hpp"
 #include "base/convert.hpp"
 #include "base/objectlock.hpp"
@@ -43,7 +42,6 @@
 #include "base/timer.hpp"
 #include "base/initialize.hpp"
 #include <boost/algorithm/string/classification.hpp>
-#include <boost/foreach.hpp>
 #include <boost/algorithm/string/replace.hpp>
 #include <boost/algorithm/string/split.hpp>
 #include <boost/algorithm/string/join.hpp>
@@ -52,40 +50,10 @@ using namespace icinga;
 
 static int l_ExternalCommands = 0;
 static boost::mutex l_QueryMutex;
-static std::map<String, LivestatusScriptFrame> l_LivestatusScriptFrames;
-static Timer::Ptr l_FrameCleanupTimer;
-static boost::mutex l_LivestatusScriptMutex;
-
-static void ScriptFrameCleanupHandler(void)
-{
-       boost::mutex::scoped_lock lock(l_LivestatusScriptMutex);
-
-       std::vector<String> cleanup_keys;
-
-       typedef std::pair<String, LivestatusScriptFrame> KVPair;
-
-       BOOST_FOREACH(const KVPair& kv, l_LivestatusScriptFrames) {
-               if (kv.second.Seen < Utility::GetTime() - 1800)
-                       cleanup_keys.push_back(kv.first);
-       }
-
-       BOOST_FOREACH(const String& key, cleanup_keys)
-               l_LivestatusScriptFrames.erase(key);
-}
-
-static void InitScriptFrameCleanup(void)
-{
-       l_FrameCleanupTimer = new Timer();
-       l_FrameCleanupTimer->OnTimerExpired.connect(boost::bind(ScriptFrameCleanupHandler));
-       l_FrameCleanupTimer->SetInterval(30);
-       l_FrameCleanupTimer->Start();
-}
-
-INITIALIZE_ONCE(InitScriptFrameCleanup);
 
 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()))
+       m_LogTimeFrom(0), m_LogTimeUntil(static_cast<long>(Utility::GetTime()))
 {
        if (lines.size() == 0) {
                m_Verb = "ERROR";
@@ -95,7 +63,7 @@ LivestatusQuery::LivestatusQuery(const std::vector<String>& lines, const String&
        }
 
        String msg;
-       BOOST_FOREACH(const String& line, lines) {
+       for (const String& line : lines) {
                msg += line + "\n";
        }
        Log(LogDebug, "LivestatusQuery", msg);
@@ -103,10 +71,10 @@ LivestatusQuery::LivestatusQuery(const std::vector<String>& lines, const String&
        m_CompatLogPath = compat_log_path;
 
        /* default separators */
-       m_Separators.push_back("\n");
-       m_Separators.push_back(";");
-       m_Separators.push_back(",");
-       m_Separators.push_back("|");
+       m_Separators.emplace_back("\n");
+       m_Separators.emplace_back(";");
+       m_Separators.emplace_back(",");
+       m_Separators.emplace_back("|");
 
        String line = lines[0];
 
@@ -123,16 +91,6 @@ LivestatusQuery::LivestatusQuery(const std::vector<String>& lines, const String&
        if (m_Verb == "COMMAND") {
                m_KeepAlive = true;
                m_Command = target;
-       } else if (m_Verb == "SCRIPT") {
-               m_Session = target;
-
-               for (unsigned int i = 1; i < lines.size(); i++) {
-                       if (m_Command != "")
-                               m_Command += "\n";
-                       m_Command += lines[i];
-               }
-
-               return;
        } else if (m_Verb == "GET") {
                m_Table = target;
        } else {
@@ -252,11 +210,11 @@ LivestatusQuery::LivestatusQuery(const std::vector<String>& lines, const String&
                        if (header == "Or" || header == "StatsOr") {
                                filter = new OrFilter();
                                Log(LogDebug, "LivestatusQuery")
-                                   << "Add OR filter for " << params << " column(s). " << deq.size() << " filters available.";
+                                       << "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.";
+                                       << "Add AND filter for " << params << " column(s). " << deq.size() << " filters available.";
                        }
 
                        if (num > deq.size()) {
@@ -269,13 +227,13 @@ LivestatusQuery::LivestatusQuery(const std::vector<String>& lines, const String&
                        while (num > 0 && num--) {
                                filter->AddSubFilter(deq.back());
                                Log(LogDebug, "LivestatusQuery")
-                                   << "Add " << num << " filter.";
+                                       << "Add " << num << " filter.";
                                deq.pop_back();
                                if (&deq == &stats)
                                        aggregators.pop_back();
                        }
 
-                       deq.push_back(filter);
+                       deq.emplace_back(filter);
                        if (&deq == &stats) {
                                Aggregator::Ptr aggregator = new CountAggregator();
                                aggregator->SetFilter(filter);
@@ -313,7 +271,7 @@ LivestatusQuery::LivestatusQuery(const std::vector<String>& lines, const String&
        /* Combine all top-level filters into a single filter. */
        AndFilter::Ptr top_filter = new AndFilter();
 
-       BOOST_FOREACH(const Filter::Ptr& filter, filters) {
+       for (const Filter::Ptr& filter : filters) {
                top_filter->AddSubFilter(filter);
        }
 
@@ -321,7 +279,7 @@ LivestatusQuery::LivestatusQuery(const std::vector<String>& lines, const String&
        m_Aggregators.swap(aggregators);
 }
 
-int LivestatusQuery::GetExternalCommands(void)
+int LivestatusQuery::GetExternalCommands()
 {
        boost::mutex::scoped_lock lock(l_QueryMutex);
 
@@ -351,18 +309,18 @@ Filter::Ptr LivestatusQuery::ParseFilter(const String& params, unsigned long& fr
                        break;
                }
 
-               tokens.push_back(temp_buffer.SubStr(0, sp_index));
+               tokens.emplace_back(temp_buffer.SubStr(0, sp_index));
                temp_buffer = temp_buffer.SubStr(sp_index + 1);
        }
 
        /* add the rest as value */
-       tokens.push_back(temp_buffer);
+       tokens.emplace_back(std::move(temp_buffer));
 
        if (tokens.size() == 2)
-               tokens.push_back("");
+               tokens.emplace_back("");
 
        if (tokens.size() < 3)
-               return Filter::Ptr();
+               return nullptr;
 
        bool negate = false;
        String attr = tokens[0];
@@ -398,7 +356,7 @@ Filter::Ptr LivestatusQuery::ParseFilter(const String& params, unsigned long& fr
        }
 
        Log(LogDebug, "LivestatusQuery")
-           << "Parsed filter with attr: '" << attr << "' op: '" << op << "' val: '" << val << "'.";
+               << "Parsed filter with attr: '" << attr << "' op: '" << op << "' val: '" << val << "'.";
 
        return filter;
 }
@@ -421,7 +379,7 @@ void LivestatusQuery::AppendResultRow(std::ostream& fp, const Array::Ptr& row, b
                bool first = true;
 
                ObjectLock rlock(row);
-               BOOST_FOREACH(const Value& value, row) {
+               for (const Value& value : row) {
                        if (first)
                                first = false;
                        else
@@ -454,7 +412,7 @@ void LivestatusQuery::PrintCsvArray(std::ostream& fp, const Array::Ptr& array, i
        bool first = true;
 
        ObjectLock olock(array);
-       BOOST_FOREACH(const Value& value, array) {
+       for (const Value& value : array) {
                if (first)
                        first = false;
                else
@@ -475,7 +433,7 @@ void LivestatusQuery::PrintPythonArray(std::ostream& fp, const Array::Ptr& rs) c
 
        bool first = true;
 
-       BOOST_FOREACH(const Value& value, rs) {
+       for (const Value& value : rs) {
                if (first)
                        first = false;
                else
@@ -501,7 +459,7 @@ String LivestatusQuery::QuoteStringPython(const String& str) {
 void LivestatusQuery::ExecuteGetHelper(const Stream::Ptr& stream)
 {
        Log(LogNotice, "LivestatusQuery")
-           << "Table: " << m_Table;
+               << "Table: " << m_Table;
 
        Table::Ptr table = Table::GetByName(m_Table, m_CompatLogPath, m_LogTimeFrom, m_LogTimeUntil);
 
@@ -531,15 +489,15 @@ void LivestatusQuery::ExecuteGetHelper(const Stream::Ptr& stream)
                std::vector<ColumnPair> column_objs;
                column_objs.reserve(columns.size());
 
-               BOOST_FOREACH(const String& columnName, columns)
-                       column_objs.push_back(std::make_pair(columnName, table->GetColumn(columnName)));
+               for (const String& columnName : columns)
+                       column_objs.emplace_back(columnName, table->GetColumn(columnName));
 
-               BOOST_FOREACH(const LivestatusRowValue& object, objects) {
+               for (const LivestatusRowValue& object : objects) {
                        Array::Ptr row = new Array();
 
                        row->Reserve(column_objs.size());
 
-                       BOOST_FOREACH(const ColumnPair& cv, column_objs) {
+                       for (const ColumnPair& cv : column_objs) {
                                if (m_ColumnHeaders)
                                        header->Add(cv.first);
 
@@ -554,24 +512,39 @@ void LivestatusQuery::ExecuteGetHelper(const Stream::Ptr& stream)
                        AppendResultRow(result, row, first_row);
                }
        } else {
-               std::vector<double> stats(m_Aggregators.size(), 0);
-               int index = 0;
+               std::map<std::vector<Value>, std::vector<AggregatorState *> > allStats;
 
                /* add aggregated stats */
-               BOOST_FOREACH(const Aggregator::Ptr aggregator, m_Aggregators) {
-                       BOOST_FOREACH(const LivestatusRowValue& object, objects) {
-                               aggregator->Apply(table, object.Row);
+               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));
                        }
 
-                       stats[index] = aggregator->GetResult();
-                       index++;
+                       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) {
                        Array::Ptr header = new Array();
 
-                       BOOST_FOREACH(const String& columnName, m_Columns) {
+                       for (const String& columnName : m_Columns) {
                                header->Add(columnName);
                        }
 
@@ -582,28 +555,33 @@ void LivestatusQuery::ExecuteGetHelper(const Stream::Ptr& stream)
                        AppendResultRow(result, header, first_row);
                }
 
-               Array::Ptr row = new Array();
+               for (const auto& kv : allStats) {
+                       Array::Ptr row = new Array();
 
-               row->Reserve(m_Columns.size() + m_Aggregators.size());
+                       row->Reserve(m_Columns.size() + m_Aggregators.size());
 
-               /*
-                * add selected columns next to stats
-                * may not be accurate for grouping!
-                */
-               if (objects.size() > 0 && m_Columns.size() > 0) {
-                       BOOST_FOREACH(const String& columnName, m_Columns) {
-                               Column column = table->GetColumn(columnName);
+                       for (const Value& keyPart : kv.first) {
+                               row->Add(keyPart);
+                       }
 
-                               LivestatusRowValue object = objects[0]; //first object wins
+                       auto& stats = kv.second;
 
-                               row->Add(column.ExtractValue(object.Row, object.GroupByType, object.GroupByObject));
-                       }
+                       for (size_t i = 0; i < m_Aggregators.size(); i++)
+                               row->Add(m_Aggregators[i]->GetResultAndFreeState(stats[i]));
+
+                       AppendResultRow(result, row, first_row);
                }
 
-               for (size_t i = 0; i < m_Aggregators.size(); i++)
-                       row->Add(stats[i]);
+               /* add a bogus zero value if aggregated is empty*/
+               if (allStats.empty()) {
+                       Array::Ptr row = new Array();
 
-               AppendResultRow(result, row, first_row);
+                       for (size_t i = 1; i <= m_Aggregators.size(); i++) {
+                               row->Add(0);
+                       }
+
+                       AppendResultRow(result, row, first_row);
+               }
        }
 
        EndResultSet(result);
@@ -620,63 +598,15 @@ void LivestatusQuery::ExecuteCommandHelper(const Stream::Ptr& stream)
        }
 
        Log(LogNotice, "LivestatusQuery")
-           << "Executing command: " << m_Command;
+               << "Executing command: " << m_Command;
        ExternalCommandProcessor::Execute(m_Command);
        SendResponse(stream, LivestatusErrorOK, "");
 }
 
-void LivestatusQuery::ExecuteScriptHelper(const Stream::Ptr& stream)
-{
-       Log(LogInformation, "LivestatusQuery")
-           << "Executing expression: " << m_Command;
-
-       m_ResponseHeader = "fixed16";
-
-       LivestatusScriptFrame& lsf = l_LivestatusScriptFrames[m_Session];
-       lsf.Seen = Utility::GetTime();
-
-       if (!lsf.Locals)
-               lsf.Locals = new Dictionary();
-
-       String fileName = "<" + Convert::ToString(lsf.NextLine) + ">";
-       lsf.NextLine++;
-
-       lsf.Lines[fileName] = m_Command;
-
-       Expression *expr = NULL;
-       Value result;
-       try {
-               expr = ConfigCompiler::CompileText(fileName, m_Command);
-               ScriptFrame frame;
-               frame.Locals = lsf.Locals;
-               frame.Self = lsf.Locals;
-               result = expr->Evaluate(frame);
-       } catch (const ScriptError& ex) {
-               delete expr;
-
-               DebugInfo di = ex.GetDebugInfo();
-
-               std::ostringstream msgbuf;
-
-               msgbuf << di.Path << ": " << lsf.Lines[di.Path] << "\n"
-                   << String(di.Path.GetLength() + 2, ' ')
-                   << String(di.FirstColumn, ' ') << String(di.LastColumn - di.FirstColumn + 1, '^') << "\n"
-                   << ex.what() << "\n";
-
-               SendResponse(stream, LivestatusErrorQuery, msgbuf.str());
-               return;
-       } catch (...) {
-               delete expr;
-               throw;
-       }
-       delete expr;
-       SendResponse(stream, LivestatusErrorOK, JsonEncode(Serialize(result, FAEphemeral | FAState | FAConfig), true));
-}
-
 void LivestatusQuery::ExecuteErrorHelper(const Stream::Ptr& stream)
 {
        Log(LogDebug, "LivestatusQuery")
-           << "ERROR: Code: '" << m_ErrorCode << "' Message: '" << m_ErrorMessage << "'.";
+               << "ERROR: Code: '" << m_ErrorCode << "' Message: '" << m_ErrorMessage << "'.";
        SendResponse(stream, m_ErrorCode, m_ErrorMessage);
 }
 
@@ -714,14 +644,12 @@ bool LivestatusQuery::Execute(const Stream::Ptr& stream)
 {
        try {
                Log(LogNotice, "LivestatusQuery")
-                   << "Executing livestatus query: " << m_Verb;
+                       << "Executing livestatus query: " << m_Verb;
 
                if (m_Verb == "GET")
                        ExecuteGetHelper(stream);
                else if (m_Verb == "COMMAND")
                        ExecuteCommandHelper(stream);
-               else if (m_Verb == "SCRIPT")
-                       ExecuteScriptHelper(stream);
                else if (m_Verb == "ERROR")
                        ExecuteErrorHelper(stream);
                else