Module: check_mk
Branch: master
Commit: 0144523c644a81eec44c1c136fdfcfc51df42949
URL:
http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=0144523c644a81…
Author: Sergey Kipnis <sk(a)mathias-kettner.de>
Date: Fri Mar 29 15:56:54 2019 +0100
[CMK-1866] - output tables for WMI are modified
- column WMIStatus added with possible content either Timeout or OK
- on WMI timeout send cached data
- if cached data is absent then send nothing
Change-Id: I63a60c90f977425b09f7c1e3715f71496871d41d
---
agents/windows/sections/SectionWMI.cc | 102 ++++++++++++++++++++++++++++++++--
agents/windows/sections/SectionWMI.h | 2 +
agents/windows/wmiHelper.cc | 16 +++++-
3 files changed, 115 insertions(+), 5 deletions(-)
diff --git a/agents/windows/sections/SectionWMI.cc
b/agents/windows/sections/SectionWMI.cc
index 4c6f1a0..3bace67 100644
--- a/agents/windows/sections/SectionWMI.cc
+++ b/agents/windows/sections/SectionWMI.cc
@@ -114,7 +114,86 @@ void SectionWMI::suspend(int duration) {
_disabled_until = time(nullptr) + duration;
}
-bool SectionWMI::produceOutputInner(std::ostream &out,
+// ********************************************************
+// copy pasted from the new agent, unit-tested in new Agent
+// ********************************************************
+namespace cma::tools {
+inline std::vector<std::string> SplitString(const std::string &In,
+ const std::string Delim,
+ int MaxCount = 0) noexcept {
+ // sanity
+ if (In.empty()) return {};
+ if (Delim.empty()) return {In};
+
+ size_t start = 0U;
+ std::vector<std::string> result;
+
+ auto end = In.find(Delim);
+ while (end != std::string::npos) {
+ result.push_back(In.substr(start, end - start));
+
+ start = end + Delim.length();
+ end = In.find(Delim, start);
+
+ // check for a skipping rest
+ if (result.size() == static_cast<size_t>(MaxCount)) {
+ end = std::string::npos;
+ break;
+ }
+ }
+
+ auto last_string = In.substr(start, end);
+ if (!last_string.empty()) result.push_back(last_string);
+
+ return result;
+}
+} // namespace cma::tools
+
+// ********************************************************
+// copy pasted from the new agent, unit-tested in new Agent
+// ********************************************************
+// adds to the output Table from the WMI WMIStatus column
+// column value is either Timeout or OK
+// Before
+// Name,Freq
+// Total,1500
+// AFter
+// Name,Freq,WMIStatus
+// Total,1500,OK
+// Empty or quite short strings are replaced empty string
+std::string WmiPostProcess(const std::string &In, bool ExceptionOn,
+ char Separator) {
+ if (In.size() < 5) { // 5 is meaningless, just anything low
+ // data absent
+ return ExceptionOn ? std::string() : In;
+ }
+
+ std::string tail_0;
+ tail_0 += Separator;
+ tail_0 += "WMIStatus\n";
+
+ std::string tail_other;
+ tail_other += Separator;
+ tail_other += ExceptionOn ? "Timeout\n" : "OK\n";
+
+ auto table = cma::tools::SplitString(In, "\n");
+ size_t s_required = 0;
+ table[0] += tail_0;
+ s_required += table[0].size();
+ for (size_t i = 1; i < table.size(); ++i) {
+ table[i] += tail_other;
+ s_required += table[i].size();
+ }
+
+ std::string out;
+ out.reserve(s_required);
+ for (const auto line : table) {
+ out += line;
+ }
+ return out;
+}
+
+bool SectionWMI::produceOutputInner(std::ostream &Out,
const std::optional<std::string> &) {
Debug(_logger) << "SectionWMI::produceOutputInner";
@@ -124,6 +203,8 @@ bool SectionWMI::produceOutputInner(std::ostream &out,
bool success = true;
+ bool exception_on = false;
+
try {
if (_helper.get() == nullptr) {
_helper.reset(
@@ -150,13 +231,26 @@ bool SectionWMI::produceOutputInner(std::ostream &out,
suspend(3600);
}
+ std::stringstream out;
outputTable(out, result);
+ cached_ = out.str();
} catch (const wmi::Timeout &t) {
- // Output WMI timeout so that the check in question knows to handle it.
- out << t.what() << std::endl;
- Debug(_logger) << "SectionWMI::produceOutputInner caught "
<< t.what();
+ exception_on = true;
+ // only logging
+ if (cached_.size()) {
+ Debug(_logger) << "SectionWMI::produceOutputInner caught "
+ << t.what() << " cached data reused";
+ } else {
+ Debug(_logger) << "SectionWMI::produceOutputInner caught "
+ << t.what();
+ }
success = true;
}
+ // in cache we always have last valid data. Or nothing.
+ // those cached data should be decorated with new column
+ auto modified = WmiPostProcess(cached_, exception_on, ',');
+ if (modified.size()) Out << modified;
+
return success;
}
diff --git a/agents/windows/sections/SectionWMI.h b/agents/windows/sections/SectionWMI.h
index 1eb1b68..801c501 100644
--- a/agents/windows/sections/SectionWMI.h
+++ b/agents/windows/sections/SectionWMI.h
@@ -57,6 +57,8 @@ private:
bool _toggle_if_missing{false};
time_t _disabled_until{0};
std::unique_ptr<wmi::Helper> _helper;
+
+ std::string cached_; // last output stored here, may be reused on timeout
};
#endif // SectionWMI_h
diff --git a/agents/windows/wmiHelper.cc b/agents/windows/wmiHelper.cc
index 482f8b2..fa52685 100644
--- a/agents/windows/wmiHelper.cc
+++ b/agents/windows/wmiHelper.cc
@@ -221,6 +221,13 @@ vector<wstring> Result::names() const {
return result;
}
+#if 0
+// testing code
+// we do not want to loose our time for
+// writing real tests for legacy product
+bool GlobalTimeout = false;
+#endif
+
bool Result::next() {
Debug(_logger) << "Result::next";
if (_enumerator == nullptr) {
@@ -229,8 +236,15 @@ bool Result::next() {
IWbemClassObject *obj = nullptr;
ULONG numReturned = 0;
- // always retrieve only one element
+
HRESULT res = _enumerator->Next(2500, 1, &obj, &numReturned);
+#if 0
+ // this portion of code is used as embedded testing tool
+ // looks terrible, still usable
+ namespace fs = std::filesystem;
+ std::error_code ec;
+ if (fs::exists("timeout.txt", ec) || GlobalTimeout) res = WBEM_S_TIMEDOUT;
+#endif
switch (res) {
case WBEM_NO_ERROR: