]> granicus.if.org Git - icinga2/commitdiff
Add check_perfmon
authorJean Flach <jean-marcel.flach@netways.de>
Mon, 23 Mar 2015 12:09:29 +0000 (13:09 +0100)
committerGunnar Beutner <gunnar@beutner.name>
Mon, 27 Apr 2015 09:22:02 +0000 (11:22 +0200)
fixes #8809

plugins/CMakeLists.txt
plugins/check_perfmon.cpp [new file with mode: 0644]
plugins/check_perfmon.h [new file with mode: 0644]

index 050b71b6c8ed0d7c56485daff8772e4b65abf2f9..049cabad3475d8b3be6c9aab6641e8c843ddd2e0 100644 (file)
@@ -27,8 +27,8 @@ if ( WIN32 )
   )
   
   list( APPEND check_SOURCES 
-    check_disk.cpp check_load.cpp check_memory.cpp check_network.cpp check_ping.cpp check_procs.cpp
-    check_service.cpp check_swap.cpp check_update.cpp check_uptime.cpp check_users.cpp )
+    check_disk.cpp check_load.cpp check_memory.cpp check_network.cpp check_ping.cpp check_perfmon.cpp
+    check_procs.cpp check_service.cpp check_swap.cpp check_update.cpp check_uptime.cpp check_users.cpp )
   
   foreach ( source ${check_SOURCES} ) 
     string ( REGEX REPLACE ".cpp\$" "" check_OUT "${source}" )
@@ -45,14 +45,15 @@ if ( WIN32 )
 
   target_link_libraries( check_load Pdh.lib )
   target_link_libraries( check_network Pdh.lib )
+  target_link_libraries ( check_perfmon Pdh.lib )
   target_link_libraries( check_ping Ntdll.lib iphlpapi.lib Ws2_32.lib )
   target_link_libraries( check_procs Pdh.lib )
   target_link_libraries( check_uptime ${Boost_SYSTEM_LIBRARY} )
   target_link_libraries( check_users wtsapi32.lib )
 
   install (
-    TARGETS check_disk check_load  check_memory check_network check_procs check_ping
-      check_service check_swap check_update check_uptime check_users
+    TARGETS check_disk check_load check_memory check_network check_procs check_perfmon
+      check_ping check_service check_swap check_update check_uptime check_users
       RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} )
 
 endif()
diff --git a/plugins/check_perfmon.cpp b/plugins/check_perfmon.cpp
new file mode 100644 (file)
index 0000000..a068b11
--- /dev/null
@@ -0,0 +1,403 @@
+/******************************************************************************
+* Icinga 2                                                                   *
+* Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org)    *
+*                                                                            *
+* This program is free software; you can redistribute it and/or              *
+* modify it under the terms of the GNU General Public License                *
+* as published by the Free Software Foundation; either version 2             *
+* of the License, or (at your option) any later version.                     *
+*                                                                            *
+* This program is distributed in the hope that it will be useful,            *
+* but WITHOUT ANY WARRANTY; without even the implied warranty of             *
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
+* GNU General Public License for more details.                               *
+*                                                                            *
+* You should have received a copy of the GNU General Public License          *
+* along with this program; if not, write to the Free Software Foundation     *
+* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
+******************************************************************************/
+
+#include <Shlwapi.h>
+#include <iostream>
+#include <vector>
+
+#include "check_perfmon.h"
+
+#define VERSION 1.0
+
+namespace po = boost::program_options;
+
+INT wmain(INT argc, WCHAR **argv)
+{
+       po::variables_map variables_map;
+       printInfoStruct stPrintInfo;
+       if (!ParseArguments(argc, argv, variables_map, stPrintInfo))
+               return 3;
+
+       if (variables_map.count("print-objects")) {
+               PrintObjects();
+               return 0;
+       }
+
+       if (variables_map.count("print-object-info")) {
+               PrintObjectInfo(stPrintInfo);
+               return 0;
+       }
+
+       if (QueryPerfData(stPrintInfo))
+               return PrintOutput(variables_map, stPrintInfo);
+       else
+               return 3;
+}
+
+BOOL ParseArguments(CONST INT ac, WCHAR **av, po::variables_map& vm, printInfoStruct& printInfo)
+{
+       WCHAR szNamePath[MAX_PATH + 1];
+       GetModuleFileName(NULL, szNamePath, MAX_PATH);
+       WCHAR *szProgName = PathFindFileName(szNamePath);
+
+       po::options_description desc("Options");
+       desc.add_options()
+               ("help,h", "Print help page and exit")
+               ("version,V", "Print version and exit")
+               ("warning,w", po::wvalue<std::wstring>(), "Warning thershold")
+               ("critical,c", po::wvalue<std::wstring>(), "Critical threshold")
+               ("performance-counter,P", po::wvalue<std::wstring>(), "The performance counter string to use")
+               ("performance-wait", po::value<DWORD>(), "Sleep in milliseconds between the two perfomance querries (Default: 1000ms)")
+               ("fmt-countertype", po::wvalue<std::wstring>(), "Value type of counter: 'double'(default), 'long', 'int64'")
+               ("print-objects", "Prints all available objects to console")
+               ("print-object-info", "Prints all available instances and counters of --performance-counter, do not use a full perfomance counter string here")
+               ;
+
+       po::basic_command_line_parser<wchar_t> parser(ac, av);
+
+       try {
+               po::store(
+                       parser
+                       .options(desc)
+                       .style(
+                       po::command_line_style::unix_style |
+                       po::command_line_style::allow_long_disguise)
+                       .run(),
+                       vm);
+               vm.notify();
+       } catch (std::exception& e) {
+               std::cout << e.what() << '\n' << desc << '\n';
+               return FALSE;
+       }
+
+       if (vm.count("version")) {
+               std::wcout << "Version: " << VERSION << '\n';
+               return FALSE;
+       }
+
+       if (vm.count("help")) {
+               std::wcout << szProgName << " Help\n\tVersion: " << VERSION << '\n';
+               wprintf(
+                       L"%s runs a check against a performance counter.\n"
+                       L"You can use the following options to define its behaviour:\n\n", szProgName);
+               std::cout << desc;
+               wprintf(
+                       L"\nIt will then output a string looking something like this:\n\n"
+                       L"\tPERFMON CRITICAL \"\\Processor(_Total)\\%% Idle Time\" = 40.34 | "
+                       L"perfmon=40.34;20;40;; \"\\Processor(_Total)\\%% Idle Time\"=40.34\n\n"
+                       L"\"tPERFMON\" being the type of the check, \"CRITICAL\" the returned status\n"
+                       L"and \"40.34\" is the performance counters value.\n"
+                       L"%s' exit codes denote the following:\n"
+                       L" 0\tOK,\n\tNo Thresholds were exceeded\n"
+                       L" 1\tWARNING,\n\tThe warning was broken, but not the critical threshold\n"
+                       L" 2\tCRITICAL,\n\tThe critical threshold was broken\n"
+                       L" 3\tUNKNOWN, \n\tNo check could be performed\n\n"
+                       , szProgName);
+               return 0;
+       }
+
+       if (vm.count("warning")) {
+               try {
+                       printInfo.tWarn = threshold(vm["warning"].as<std::wstring>());
+               } catch (std::invalid_argument& e) {
+                       std::wcout << e.what() << '\n';
+                       return FALSE;
+               }
+       }
+
+       if (vm.count("critical")) {
+               try {
+                       printInfo.tCrit = threshold(vm["critical"].as<std::wstring>());
+               } catch (std::invalid_argument& e) {
+                       std::wcout << e.what() << '\n';
+                       return FALSE;
+               }
+       }
+
+       if (vm.count("fmt-countertype")) {
+               if (vm["fmt-countertype"].as<std::wstring>().compare(L"double"))
+                       printInfo.dwRequestedType = PDH_FMT_DOUBLE;
+               else if (vm["fmt-countertype"].as<std::wstring>().compare(L"int64"))
+                       printInfo.dwRequestedType = PDH_FMT_LARGE;
+               else if (vm["fmt-countertype"].as<std::wstring>().compare(L"long"))
+                       printInfo.dwRequestedType = PDH_FMT_LONG;
+               else {
+                       std::wcout << "Unknown value type " << vm["fmt-countertype"].as<std::wstring>() << '\n';
+                       return FALSE;
+               }
+       }
+
+       if (vm.count("performance-counter"))
+               printInfo.wsFullPath = vm["performance-counter"].as<std::wstring>();
+
+       if (vm.count("performance-wait"))
+               printInfo.dwPerformanceWait = vm["performance-wait"].as<DWORD>();
+
+       return TRUE;
+}
+
+BOOL GetIntstancesAndCountersOfObject(CONST std::wstring wsObject, 
+                                                                        std::vector<std::wstring>& vecInstances, 
+                                                                        std::vector<std::wstring>& vecCounters)
+{
+       LPWSTR szDataSource = NULL, szMachineName = NULL,
+               mszCounterList = NULL, mszInstanceList = NULL;
+       DWORD dwCounterListLength = 0, dwInstanceListLength = 0;
+
+       std::wstringstream wssInstanceName, wssCounterName;
+       LPWSTR szObjectName = new WCHAR[wsObject.length() + 1];
+       StrCpyW(szObjectName, wsObject.c_str());
+
+       PDH_STATUS status =
+               PdhEnumObjectItems(szDataSource, szMachineName, szObjectName,
+               mszCounterList, &dwCounterListLength, mszInstanceList,
+               &dwInstanceListLength, PERF_DETAIL_WIZARD, 0);
+
+       if (status != PDH_MORE_DATA) {
+               delete[]szObjectName;
+               return FALSE;
+       }
+
+       mszCounterList = new WCHAR[dwCounterListLength + 1];
+       mszInstanceList = new WCHAR[dwInstanceListLength + 1];
+
+       status = PdhEnumObjectItems(szDataSource, szMachineName, szObjectName,
+                                                               mszCounterList, &dwCounterListLength, mszInstanceList,
+                                                               &dwInstanceListLength, PERF_DETAIL_WIZARD, 0);
+
+       if (FAILED(status)) {
+               delete[]mszCounterList;
+               delete[]mszInstanceList;
+               delete[]szObjectName;
+               return FALSE;
+       }
+
+       if (dwInstanceListLength) {
+               for (DWORD c = 0; c < dwInstanceListLength-1; ++c) {
+                       if (mszInstanceList[c])
+                               wssInstanceName << mszInstanceList[c];
+                       else {
+                               vecInstances.push_back(wssInstanceName.str());
+                               wssInstanceName.str(L"");
+                       }
+               }
+       }
+
+       if (dwCounterListLength) {
+               for (DWORD c = 0; c < dwCounterListLength-1; ++c) {
+                       if (mszCounterList[c]) {
+                               wssCounterName << mszCounterList[c];
+                       } else {
+                               vecCounters.push_back(wssCounterName.str());
+                               wssCounterName.str(L"");
+                       }
+               }
+       }
+
+       delete[]mszCounterList;
+       delete[]mszInstanceList;
+       delete[]szObjectName;
+
+       return TRUE;
+}
+
+VOID PrintObjects()
+{
+       LPWSTR szDataSource = NULL, szMachineName = NULL, mszObjectList = NULL;
+       DWORD dwBufferLength = 0;
+       PDH_STATUS status =
+               PdhEnumObjects(szDataSource, szMachineName, mszObjectList,
+               &dwBufferLength, PERF_DETAIL_WIZARD, FALSE);
+       //HEX HEX! Only a Magicians gets all the info he wants, and only Microsoft knows what that means
+
+       if (status != PDH_MORE_DATA)
+               goto die;
+
+       mszObjectList = new WCHAR[dwBufferLength + 2];
+       status = PdhEnumObjects(szDataSource, szMachineName, mszObjectList,
+                                                       &dwBufferLength, PERF_DETAIL_WIZARD, FALSE);
+
+       if (FAILED(status))
+               goto die;
+
+       DWORD c = 0;
+
+       while (++c < dwBufferLength) {
+               if (mszObjectList[c] == '\0')
+                       std::wcout << '\n';
+               else
+                       std::wcout << mszObjectList[c];
+       }
+
+       delete[]mszObjectList;
+       return;
+
+die:
+       FormatPDHError(status);
+       delete[]mszObjectList;
+}
+
+VOID PrintObjectInfo(CONST printInfoStruct& pI)
+{
+       if (pI.wsFullPath.empty()) {
+               std::wcout << "No object given!\n";
+               return;
+       }
+
+       std::vector<std::wstring> vecInstances, vecCounters;
+
+       if (!GetIntstancesAndCountersOfObject(pI.wsFullPath, vecInstances, vecCounters)) {
+               std::wcout << "Could not enumerate instances and counters of " << pI.wsFullPath << '\n'
+                   << "Make sure it exists!\n";
+               return;
+       }
+
+       std::wcout << "Instances of " << pI.wsFullPath << ":\n";
+       if (vecInstances.empty())
+               std::wcout << "> Has no instances\n";
+       else {
+               for (std::vector<std::wstring>::iterator it = vecInstances.begin();
+                        it != vecInstances.end(); ++it) {
+                       std::wcout << "> " << *it << '\n';
+               }
+       }
+       std::wcout << std::endl;
+
+       std::wcout << "Performance Counters of " << pI.wsFullPath << ":\n";
+       if (vecCounters.empty())
+               std::wcout << "> Has no counters\n";
+       else {
+               for (std::vector<std::wstring>::iterator it = vecCounters.begin();
+                        it != vecCounters.end(); ++it) {
+                       std::wcout << "> " << *it << '\n';
+               }
+       }
+       std::wcout << std::endl;
+}
+
+BOOL QueryPerfData(printInfoStruct& pI)
+{
+       PDH_HQUERY hQuery = NULL;
+       PDH_HCOUNTER hCounter = NULL;
+       PDH_FMT_COUNTERVALUE_ITEM *pDisplayValues = NULL;
+       DWORD dwBufferSize = 0, dwItemCount = 0;
+
+       if (pI.wsFullPath.empty()) {
+               std::wcout << "No performance counter path given!\n";
+               return FALSE;
+       }
+
+       PDH_STATUS status = PdhOpenQuery(NULL, NULL, &hQuery);
+       if (FAILED(status))
+               goto die;
+
+       status = PdhAddCounter(hQuery, pI.wsFullPath.c_str(), NULL, &hCounter);
+       if (FAILED(status))
+               goto die;
+
+       status = PdhCollectQueryData(hQuery);
+       if (FAILED(status))
+               goto die;
+
+       /* 
+       /* Most counters need two queries to provide a value.
+       /* Those which need only one will return the second.
+        */
+       Sleep(pI.dwPerformanceWait);
+
+       status = PdhCollectQueryData(hQuery);
+       if (FAILED(status))
+               goto die;
+
+       status = PdhGetFormattedCounterArray(hCounter, pI.dwRequestedType,
+                                                                                &dwBufferSize, &dwItemCount, pDisplayValues);
+       if (status != PDH_MORE_DATA)
+               goto die;
+
+       pDisplayValues = reinterpret_cast<PDH_FMT_COUNTERVALUE_ITEM*>(new BYTE[dwBufferSize]);
+       status = PdhGetFormattedCounterArray(hCounter, pI.dwRequestedType,
+                                                                                &dwBufferSize, &dwItemCount, pDisplayValues);
+
+       if (FAILED(status))
+               goto die;
+
+       switch (pI.dwRequestedType)
+       {
+       case (PDH_FMT_LONG):
+               pI.dValue = pDisplayValues[0].FmtValue.longValue;
+       case (PDH_FMT_LARGE) :
+               pI.dValue = pDisplayValues[0].FmtValue.largeValue;
+       default:
+               pI.dValue = pDisplayValues[0].FmtValue.doubleValue;
+       }
+
+       delete[]pDisplayValues;
+
+       return TRUE;
+
+die:
+       FormatPDHError(status);
+       delete[]pDisplayValues;
+       return FALSE;
+}
+
+INT PrintOutput(CONST po::variables_map& vm, printInfoStruct& pi)
+{
+       std::wstringstream wssPerfData;
+       wssPerfData << "perfmon=" << pi.dValue << ';'
+               << pi.tWarn.pString() << ';' << pi.tCrit.pString() << ";; "
+               << '"' << pi.wsFullPath << "\"=" << pi.dValue;
+
+       if (pi.tCrit.rend(pi.dValue)) {
+               std::wcout << "PERFMON CRITICAL \"" << pi.wsFullPath << "\" = "
+                       << pi.dValue << " | " << wssPerfData.str() << '\n';
+               return 2;
+       }
+
+       if (pi.tWarn.rend(pi.dValue)) {
+               std::wcout << "PERFMON WARNING \"" << pi.wsFullPath << "\" = "
+                       << pi.dValue << " | " << wssPerfData.str() << '\n';
+               return 1;
+       }
+
+       std::wcout << "PERFMON OK \"" << pi.wsFullPath << "\" = "
+               << pi.dValue << " | " << wssPerfData.str() << '\n';
+       return 0;
+}
+
+VOID FormatPDHError(PDH_STATUS status)
+{
+       HANDLE hPdhLibrary = NULL;
+       LPWSTR pMessage = NULL;
+
+       hPdhLibrary = LoadLibrary(L"pdh.dll");
+       if (NULL == hPdhLibrary) {
+               std::wcout << "LoadLibrary failed with " << GetLastError() << '\n';
+               return;
+       }
+
+       if (!FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY,
+               hPdhLibrary, status, 0, (LPWSTR)&pMessage, 0, NULL)) {
+               std::wcout << "Format message failed with " << std::hex << GetLastError() << '\n';
+               return;
+       }
+
+       std::wcout << pMessage << '\n';
+       LocalFree(pMessage);
+}
\ No newline at end of file
diff --git a/plugins/check_perfmon.h b/plugins/check_perfmon.h
new file mode 100644 (file)
index 0000000..d97389c
--- /dev/null
@@ -0,0 +1,48 @@
+/******************************************************************************
+ * Icinga 2                                                                   *
+ * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org)    *
+ *                                                                            *
+ * This program is free software; you can redistribute it and/or              *
+ * modify it under the terms of the GNU General Public License                *
+ * as published by the Free Software Foundation; either version 2             *
+ * of the License, or (at your option) any later version.                     *
+ *                                                                            *
+ * This program is distributed in the hope that it will be useful,            *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
+ * GNU General Public License for more details.                               *
+ *                                                                            *
+ * You should have received a copy of the GNU General Public License          *
+ * along with this program; if not, write to the Free Software Foundation     *
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
+ ******************************************************************************/
+
+#ifndef CHECK_PERFMON_H
+#define CHECK_PERFMON_H
+
+#include <Windows.h>
+#include <Pdh.h>
+#include <pdhmsg.h>
+
+#include "thresholds.h"
+
+#include "boost/program_options.hpp"
+
+struct printInfoStruct
+{
+       threshold tWarn, tCrit;
+       std::wstring wsFullPath;
+       DOUBLE dValue;
+       DWORD dwPerformanceWait = 1000,
+               dwRequestedType = PDH_FMT_DOUBLE;
+};
+
+BOOL ParseArguments(CONST INT, WCHAR **, boost::program_options::variables_map&, printInfoStruct&);
+BOOL GetIntstancesAndCountersOfObject(CONST std::wstring, std::vector<std::wstring>&, std::vector<std::wstring>&);
+VOID PrintObjects();
+VOID PrintObjectInfo(CONST printInfoStruct&);
+INT QueryPerfData(printInfoStruct&);
+INT PrintOutput(CONST boost::program_options::variables_map&, printInfoStruct&);
+VOID FormatPDHError(PDH_STATUS);
+
+#endif // !CHECK_PERFMON_H
\ No newline at end of file