CLI: Add 'troubleshoot collect' command

By calling `icinga2 troubleshoot collect [--console]` a small file
containing basic application information and a tail of all found logs
and the latest crash report will be created [or displayed].
It does not collect config files at the moment.

refs #3446
This commit is contained in:
Jean Flach 2015-01-22 12:10:32 +01:00 committed by Michael Friedrich
parent 4a64d4991b
commit cc5a8da6e8
8 changed files with 628 additions and 96 deletions

View File

@ -20,13 +20,14 @@ set(cli_SOURCES
nodesetcommand.cpp nodesetupcommand.cpp nodeupdateconfigcommand.cpp nodewizardcommand.cpp nodeutility.cpp
clicommand.cpp
consolecommand.cpp
daemoncommand.cpp
daemoncommand.cpp daemonutility.cpp
featureenablecommand.cpp featuredisablecommand.cpp featurelistcommand.cpp featureutility.cpp
objectlistcommand.cpp
pkinewcacommand.cpp pkinewcertcommand.cpp pkisigncsrcommand.cpp pkirequestcommand.cpp pkisavecertcommand.cpp pkiticketcommand.cpp
pkiutility.cpp
repositoryclearchangescommand.cpp repositorycommitcommand.cpp repositoryobjectcommand.cpp repositoryutility.cpp
variablegetcommand.cpp variablelistcommand.cpp variableutility.cpp
troubleshootcollectcommand.cpp
)
if(ICINGA2_UNITY_BUILD)

View File

@ -18,12 +18,12 @@
******************************************************************************/
#include "cli/daemoncommand.hpp"
#include "cli/daemonutility.hpp"
#include "config/configcompiler.hpp"
#include "config/configcompilercontext.hpp"
#include "config/configitembuilder.hpp"
#include "base/logger.hpp"
#include "base/application.hpp"
#include "base/logger.hpp"
#include "base/timer.hpp"
#include "base/utility.hpp"
#include "base/exception.hpp"
@ -60,95 +60,6 @@ static String LoadAppType(const String& typeSpec)
return typeSpec.SubStr(index + 1);
}
static bool ExecuteExpression(Expression *expression)
{
if (!expression)
return false;
try {
ScriptFrame frame;
expression->Evaluate(frame);
} catch (const std::exception& ex) {
Log(LogCritical, "config", DiagnosticInformation(ex));
Application::Exit(EXIT_FAILURE);
}
return true;
}
static void IncludeZoneDirRecursive(const String& path)
{
String zoneName = Utility::BaseName(path);
std::vector<Expression *> expressions;
Utility::GlobRecursive(path, "*.conf", boost::bind(&ConfigCompiler::CollectIncludes, boost::ref(expressions), _1, zoneName), GlobFile);
DictExpression expr(expressions);
ExecuteExpression(&expr);
}
static void IncludeNonLocalZone(const String& zonePath)
{
String etcPath = Application::GetZonesDir() + "/" + Utility::BaseName(zonePath);
if (Utility::PathExists(etcPath) || Utility::PathExists(zonePath + "/.authoritative"))
return;
IncludeZoneDirRecursive(zonePath);
}
static bool LoadConfigFiles(const boost::program_options::variables_map& vm, const String& appType,
const String& objectsFile = String(), const String& varsfile = String())
{
if (!objectsFile.IsEmpty())
ConfigCompilerContext::GetInstance()->OpenObjectsFile(objectsFile);
if (vm.count("config") > 0) {
BOOST_FOREACH(const String& configPath, vm["config"].as<std::vector<std::string> >()) {
Expression *expression = ConfigCompiler::CompileFile(configPath);
ExecuteExpression(expression);
delete expression;
}
} else if (!vm.count("no-config")) {
Expression *expression = ConfigCompiler::CompileFile(Application::GetSysconfDir() + "/icinga2/icinga2.conf");
ExecuteExpression(expression);
delete expression;
}
/* Load cluster config files - this should probably be in libremote but
* unfortunately moving it there is somewhat non-trivial. */
String zonesEtcDir = Application::GetZonesDir();
if (!zonesEtcDir.IsEmpty() && Utility::PathExists(zonesEtcDir))
Utility::Glob(zonesEtcDir + "/*", &IncludeZoneDirRecursive, GlobDirectory);
String zonesVarDir = Application::GetLocalStateDir() + "/lib/icinga2/api/zones";
if (Utility::PathExists(zonesVarDir))
Utility::Glob(zonesVarDir + "/*", &IncludeNonLocalZone, GlobDirectory);
String name, fragment;
BOOST_FOREACH(boost::tie(name, fragment), ConfigFragmentRegistry::GetInstance()->GetItems()) {
Expression *expression = ConfigCompiler::CompileText(name, fragment);
ExecuteExpression(expression);
delete expression;
}
ConfigItemBuilder::Ptr builder = new ConfigItemBuilder();
builder->SetType(appType);
builder->SetName("application");
ConfigItem::Ptr item = builder->Compile();
item->Register();
bool result = ConfigItem::CommitItems();
if (!result)
return false;
ConfigCompilerContext::GetInstance()->FinishObjectsFile();
ScriptGlobal::WriteToFile(varsfile);
return true;
}
#ifndef _WIN32
static void SigHupHandler(int)
{
@ -339,7 +250,13 @@ int DaemonCommand::Run(const po::variables_map& vm, const std::vector<std::strin
}
}
if (!LoadConfigFiles(vm, appType, Application::GetObjectsPath(), Application::GetVarsPath()))
std::vector<std::string> configs;
if (vm.count("config") > 0)
configs = vm["config"].as < std::vector<std::string> >() ;
else if (!vm.count("no-config"))
configs.push_back(Application::GetSysconfDir() + "/icinga2/icinga2.conf");
if (!DaemonUtility::LoadConfigFiles(configs, appType, Application::GetObjectsPath(), Application::GetVarsPath()))
return EXIT_FAILURE;
if (vm.count("validate")) {

131
lib/cli/daemonutility.cpp Normal file
View File

@ -0,0 +1,131 @@
/******************************************************************************
* 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 "cli/daemonutility.hpp"
#include "base/utility.hpp"
#include "base/logger.hpp"
#include "base/application.hpp"
#include "config/configcompiler.hpp"
#include "config/configcompilercontext.hpp"
#include "config/configitembuilder.hpp"
using namespace icinga;
bool ExecuteExpression(Expression *expression)
{
if (!expression)
return false;
try {
ScriptFrame frame;
expression->Evaluate(frame);
} catch (const std::exception& ex) {
Log(LogCritical, "config", DiagnosticInformation(ex));
return false;
}
return true;
}
void IncludeZoneDirRecursive(const String& path, bool& success)
{
String zoneName = Utility::BaseName(path);
std::vector<Expression *> expressions;
Utility::GlobRecursive(path, "*.conf", boost::bind(&ConfigCompiler::CollectIncludes, boost::ref(expressions), _1, zoneName), GlobFile);
DictExpression expr(expressions);
if (!ExecuteExpression(&expr))
success = false;
}
void IncludeNonLocalZone(const String& zonePath, bool& success)
{
String etcPath = Application::GetZonesDir() + "/" + Utility::BaseName(zonePath);
if (Utility::PathExists(etcPath) || Utility::PathExists(zonePath + "/.authoritative"))
return;
IncludeZoneDirRecursive(zonePath, success);
}
bool DaemonUtility::ValidateConfigFiles(const std::vector<std::string>& configs, const String& objectsFile)
{
bool success;
if (!objectsFile.IsEmpty())
ConfigCompilerContext::GetInstance()->OpenObjectsFile(objectsFile);
if (!configs.empty()) {
BOOST_FOREACH(const String& configPath, configs)
{
Expression *expression = ConfigCompiler::CompileFile(configPath);
success = ExecuteExpression(expression);
delete expression;
if (!success)
return false;
}
}
/* Load cluster config files - this should probably be in libremote but
* unfortunately moving it there is somewhat non-trivial. */
String zonesEtcDir = Application::GetZonesDir();
if (!zonesEtcDir.IsEmpty() && Utility::PathExists(zonesEtcDir))
Utility::Glob(zonesEtcDir + "/*", boost::bind(&IncludeZoneDirRecursive, _1, boost::ref(success)), GlobDirectory);
if (!success)
return false;
String zonesVarDir = Application::GetLocalStateDir() + "/lib/icinga2/api/zones";
if (Utility::PathExists(zonesVarDir))
Utility::Glob(zonesVarDir + "/*", boost::bind(&IncludeNonLocalZone, _1, boost::ref(success)), GlobDirectory);
if (!success)
return false;
String name, fragment;
BOOST_FOREACH(boost::tie(name, fragment), ConfigFragmentRegistry::GetInstance()->GetItems())
{
Expression *expression = ConfigCompiler::CompileText(name, fragment);
success = ExecuteExpression(expression);
delete expression;
if (!success)
return false;
}
return true;
}
bool DaemonUtility::LoadConfigFiles(const std::vector<std::string>& configs, const String& appType,
const String& objectsFile, const String& varsfile)
{
if (!DaemonUtility::ValidateConfigFiles(configs, objectsFile))
return false;
ConfigItemBuilder::Ptr builder = new ConfigItemBuilder();
builder->SetType(appType);
builder->SetName("application");
ConfigItem::Ptr item = builder->Compile();
item->Register();
bool result = ConfigItem::CommitItems();
if (!result)
return false;
ConfigCompilerContext::GetInstance()->FinishObjectsFile();
ScriptGlobal::WriteToFile(varsfile);
}

36
lib/cli/daemonutility.hpp Normal file
View File

@ -0,0 +1,36 @@
/******************************************************************************
* 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 DAEMONUTILIT_H
#define DAEMONUTILIT_H
//#include "base/i2-base.hpp"
#include "base/string.hpp"
#include <boost/program_options.hpp>
namespace icinga
{
class DaemonUtility
{
public:
static bool ValidateConfigFiles(const std::vector<std::string>& configs, const String& objectsFile = String());
static bool LoadConfigFiles(const std::vector<std::string>& configs, const String& appType, const String& objectsFile = String(), const String& varsfile = String());
};
}
#endif /*DAEMONULITIY_H*/

View File

@ -175,7 +175,7 @@ int FeatureUtility::DisableFeatures(const std::vector<std::string>& features)
return 0;
}
int FeatureUtility::ListFeatures(void)
int FeatureUtility::ListFeatures(std::ostream& os)
{
std::vector<String> disabled_features;
std::vector<String> enabled_features;
@ -183,13 +183,13 @@ int FeatureUtility::ListFeatures(void)
if (!FeatureUtility::GetFeatures(disabled_features, true))
return 1;
std::cout << ConsoleColorTag(Console_ForegroundRed | Console_Bold) << "Disabled features: " << ConsoleColorTag(Console_Normal)
os << ConsoleColorTag(Console_ForegroundRed | Console_Bold) << "Disabled features: " << ConsoleColorTag(Console_Normal)
<< boost::algorithm::join(disabled_features, " ") << "\n";
if (!FeatureUtility::GetFeatures(enabled_features, false))
return 1;
std::cout << ConsoleColorTag(Console_ForegroundGreen | Console_Bold) << "Enabled features: " << ConsoleColorTag(Console_Normal)
os << ConsoleColorTag(Console_ForegroundGreen | Console_Bold) << "Enabled features: " << ConsoleColorTag(Console_Normal)
<< boost::algorithm::join(enabled_features, " ") << "\n";
return 0;

View File

@ -40,7 +40,7 @@ public:
static int EnableFeatures(const std::vector<std::string>& features);
static int DisableFeatures(const std::vector<std::string>& features);
static int ListFeatures(void);
static int ListFeatures(std::ostream& os = std::cout);
static bool GetFeatures(std::vector<String>& features, bool enable);

View File

@ -0,0 +1,402 @@
/*****************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2014 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 "cli/troubleshootcollectcommand.hpp"
#include "cli/featureutility.hpp"
#include "cli/daemonutility.hpp"
#include "base/netstring.hpp"
#include "base/application.hpp"
#include "base/stdiostream.hpp"
#include "base/json.hpp"
#include "base/objectlock.hpp"
#include "config/configitembuilder.hpp"
#include <boost/circular_buffer.hpp>
#include <boost/foreach.hpp>
#include <boost/algorithm/string/join.hpp>
#include <iostream>
#include <fstream>
using namespace icinga;
namespace po = boost::program_options;
REGISTER_CLICOMMAND("troubleshoot/collect", TroubleshootCollectCommand);
String TroubleshootCollectCommand::GetDescription(void) const
{
return "Collect logs and other relevant information for troubleshooting purposes.";
}
String TroubleshootCollectCommand::GetShortDescription(void) const
{
return "Collect information for troubleshooting";
}
static void GetLatestReport(const String& filename, time_t& bestTimestamp, String& bestFilename)
{
#ifdef _WIN32
struct _stat buf;
if (_stat(filename.CStr(), &buf))
return;
#else
struct stat buf;
if (stat(filename.CStr(), &buf))
return;
#endif /*_WIN32*/
if (buf.st_mtime > bestTimestamp) {
bestTimestamp = buf.st_mtime;
bestFilename = filename;
}
}
/*Print the latest crash report to *os* */
static void PrintCrashReports(std::ostream& os)
{
String spath = Application::GetLocalStateDir() + "/log/icinga2/crash/report.*";
time_t bestTimestamp = 0;
String bestFilename;
try {
Utility::Glob(spath,
boost::bind(&GetLatestReport, _1, boost::ref(bestTimestamp), boost::ref(bestFilename)), GlobFile);
}
#ifdef _WIN32
catch (win32_error &ex) {
if (int const * err = boost::get_error_info<errinfo_win32_error>(ex)) {
if (*err != 3) //Error code for path does not exist
throw ex;
os << Application::GetLocalStateDir() + "/log/icinga2/crash/ does not exist\n";
} else {
throw ex;
}
}
#else
catch (...) {
throw;
}
#endif /*_WIN32*/
if (!bestTimestamp)
os << "\nNo crash logs found in " << Application::GetLocalStateDir().CStr() << "/log/icinga2/crash/\n";
else {
const std::tm tm = Utility::LocalTime(bestTimestamp);
char *tmBuf = new char[200]; //Should always be enough
const char *fmt = "%Y-%m-%d %H:%M:%S" ;
if (!strftime(tmBuf, 199, fmt, &tm))
return;
os << "\nLatest crash report is from " << tmBuf
<< "\nFile: " << bestFilename << '\n';
TroubleshootCollectCommand::tail(bestFilename, 20, os);
}
}
/*Print the last *numLines* of *file* to *os* */
int TroubleshootCollectCommand::tail(const String& file, int numLines, std::ostream& os)
{
boost::circular_buffer<std::string> ringBuf(numLines);
std::ifstream text;
text.open(file.CStr(), std::ifstream::in);
if (!text.good())
return 0;
std::string line;
int lines = 0;
while (std::getline(text, line)) {
ringBuf.push_back(line);
lines++;
}
if (lines < numLines)
numLines = lines;
for (int k = 0; k < numLines; k++)
os << '\t' << ringBuf[k] << '\n';;
text.close();
return numLines;
}
static bool PrintIcingaConf(std::ostream& os)
{
String path = Application::GetSysconfDir() + "/icinga2/icinga2.conf";
std::ifstream text;
text.open(path.CStr(), std::ifstream::in);
if (!text.is_open()) {
Log(LogCritical, "troubleshooting", "Could not find icinga2.conf at its default location (" + path + ")");
os << "! Could not open " << path
<< "\n!\tIf you use a custom icinga2.conf provide it after validating it via `icinga2 daemon -C`"
<< "\n!\tIf you do not have a icinga2.conf you just found your problem.\n";
return false;
}
std::string line;
os << "\nFound main Icinga2 configuration file at " << path << '\n';
while (std::getline(text, line)) {
os << '\t' << line << '\n';
}
return true;
}
static bool PrintZonesConf(std::ostream& os)
{
String path = Application::GetSysconfDir() + "/icinga2/zones.conf";
std::ifstream text;
text.open(path.CStr(), std::ifstream::in);
if (!text.is_open()) {
Log(LogWarning, "troubleshooting", "Could not find zones.conf at its default location (" + path + ")");
os << "!Could not open " << path
<< "\n!\tThis could be the root of your problems, if you trying to use multiple Icinga2 instances.\n";
return false;
}
std::string line;
os << "\nFound zones configuration file at " << path << '\n';
while (std::getline(text, line)) {
os << '\t' << line << '\n';
}
return true;
}
static void ValidateConfig(std::ostream& os)
{
/* Not loading the icinga library would make config validation fail.
(Depending on the configuration and core count of your machine.) */
Logger::DisableConsoleLog();
Utility::LoadExtensionLibrary("icinga");
std::vector<std::string> configs;
configs.push_back(Application::GetSysconfDir() + "/icinga2/icinga2.conf");
if (DaemonUtility::ValidateConfigFiles(configs, Application::GetObjectsPath()))
os << "Config validation successful\n";
else
os << "! Config validation failed\n"
<< "Run `icinga2 daemon --validate` to recieve additional information\n";
}
static void CheckFeatures(std::ostream& os)
{
Dictionary::Ptr features = new Dictionary;
std::vector<String> disabled_features;
std::vector<String> enabled_features;
if (!FeatureUtility::GetFeatures(disabled_features, true)
|| !FeatureUtility::GetFeatures(enabled_features, false)) {
Log(LogWarning, "troubleshoot", "Could not collect features");
os << "! Failed to collect enabled and/or disabled features. Check\n"
<< FeatureUtility::GetFeaturesAvailablePath() << '\n'
<< FeatureUtility::GetFeaturesEnabledPath() << '\n';
return;
}
BOOST_FOREACH(const String feature, disabled_features)
features->Set(feature, false);
BOOST_FOREACH(const String feature, enabled_features)
features->Set(feature, true);
os << "Icinga2 feature list\n"
<< "Enabled features:\n\t" << boost::algorithm::join(enabled_features, " ") << '\n'
<< "Disabled features:\n\t" << boost::algorithm::join(disabled_features, " ") << '\n';
if (!features->Get("mainlog").ToBool())
os << "! mainlog is disabled, please activate it and rerun icinga2\n";
if (!features->Get("debuglog").ToBool())
os << "! debuglog is disabled, please activate it and rerun icinga2\n";
}
static void CheckObjectFile(const String& objectfile, std::ostream& os)
{
os << "Checking object file from " << objectfile << '\n';
std::fstream fp;
std::set<String> configSet;
Dictionary::Ptr typeCount = new Dictionary();
Dictionary::Ptr logPath = new Dictionary();
fp.open(objectfile.CStr(), std::ios_base::in);
if (!fp.is_open()) {
Log(LogWarning, "troubleshoot", "Could not open objectfile");
os << "! Could not open object file.\n";
return;
}
StdioStream::Ptr sfp = new StdioStream(&fp, false);
int typeL = 0, countTotal = 0;
String message;
StreamReadContext src;
while (NetString::ReadStringFromStream(sfp, &message, src) == StatusNewItem) {
Dictionary::Ptr object = JsonDecode(message);
Dictionary::Ptr properties = object->Get("properties");
String name = object->Get("name");
String type = object->Get("type");
//Find longest typename for padding
typeL = type.GetLength() > typeL ? type.GetLength() : typeL;
countTotal++;
if (!typeCount->Contains(type))
typeCount->Set(type, 1);
else
typeCount->Set(type, typeCount->Get(type)+1);
Array::Ptr debug_info = object->Get("debug_info");
if (debug_info) {
configSet.insert(debug_info->Get(0));
}
if (Utility::Match(type, "FileLogger")) {
Dictionary::Ptr debug_hints = object->Get("debug_hints");
Dictionary::Ptr properties = object->Get("properties");
ObjectLock olock(properties);
BOOST_FOREACH(const Dictionary::Pair& kv, properties) {
if (Utility::Match(kv.first, "path"))
logPath->Set(name, kv.second);
}
}
}
if (!countTotal) {
os << "! No objects found in objectfile.\n";
return;
}
//Print objects with count
os << "Found the following objects:\n"
<< "\tType" << std::string(typeL-4, ' ') << " : Count\n";
ObjectLock olock(typeCount);
BOOST_FOREACH(const Dictionary::Pair& kv, typeCount) {
os << '\t' << kv.first << std::string(typeL - kv.first.GetLength(), ' ')
<< " : " << kv.second << '\n';
}
//Print location of .config files
os << '\n' << countTotal << " objects in total, originating from these files:\n";
for (std::set<String>::iterator it = configSet.begin();
it != configSet.end(); it++)
os << '\t' << *it << '\n';
//Print tail of file loggers
if (!logPath->GetLength()) {
os << "! No loggers found, check whether you enabled any logging features\n";
} else {
os << "\nGetting the last 20 lines of the " << logPath->GetLength() << " found FileLogger objects.\n";
ObjectLock ulock(logPath);
BOOST_FOREACH(const Dictionary::Pair& kv, logPath)
{
os << "\nLogger " << kv.first << " at path: " << kv.second << "\n";
if (!TroubleshootCollectCommand::tail(kv.second, 20, os))
os << "\t" << kv.second << " either does not exist or is empty\n";
}
}
}
void TroubleshootCollectCommand::InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const
{
visibleDesc.add_options()
("console,c", "print to console instead of file")
("output-file", boost::program_options::value<std::string>(), "path to output file")
;
}
int TroubleshootCollectCommand::Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const
{
std::ofstream os;
String path;
if (vm.count("console")) {
Logger::DisableConsoleLog();
os.copyfmt(std::cout);
os.clear(std::cout.rdstate());
os.basic_ios<char>::rdbuf(std::cout.rdbuf());
} else {
if (vm.count("output-file"))
path = vm["output-file"].as<std::string>();
else
path = Application::GetLocalStateDir() +"/log/icinga2/troubleshooting.log";
os.open(path.CStr(), std::ios::out | std::ios::trunc);
if (!os.is_open()) {
Log(LogCritical, "troubleshoot", "Failed to open file to write: " + path);
return 3;
}
}
String appName = Utility::BaseName(Application::GetArgV()[0]);
os << appName << " -- Troubleshooting help:" << std::endl
<< "Should you run into problems with Icinga please add this file to your help request\n\n";
if (appName.GetLength() > 3 && appName.SubStr(0, 3) == "lt-")
appName = appName.SubStr(3, appName.GetLength() - 3);
//Application::DisplayInfoMessage() but formatted
os << "\tApplication version: " << Application::GetVersion() << "\n"
<< "\tInstallation root: " << Application::GetPrefixDir() << "\n"
<< "\tSysconf directory: " << Application::GetSysconfDir() << "\n"
<< "\tRun directory: " << Application::GetRunDir() << "\n"
<< "\tLocal state directory: " << Application::GetLocalStateDir() << "\n"
<< "\tPackage data directory: " << Application::GetPkgDataDir() << "\n"
<< "\tState path: " << Application::GetStatePath() << "\n"
<< "\tObjects path: " << Application::GetObjectsPath() << "\n"
<< "\tVars path: " << Application::GetVarsPath() << "\n"
<< "\tPID path: " << Application::GetPidPath() << "\n"
<< "\tApplication type: " << Application::GetApplicationType() << "\n";
os << '\n';
CheckFeatures(os);
os << '\n';
String objectfile = Application::GetObjectsPath();
if (!Utility::PathExists(objectfile)) {
Log(LogWarning, "troubleshoot", "Failed to open objectfile");
os << "! Cannot open object file '" << objectfile << "'."
<< "! Run 'icinga2 daemon -C' to validate config and generate the cache file.\n";
} else
CheckObjectFile(objectfile, os);
os << "\nA collection of important configuration files follows, please make sure to censor your sensible data\n";
if (PrintIcingaConf(os)) {
ValidateConfig(os);
} else {
Log(LogWarning, "troubleshoot", "Failed to open icinga2.conf");
os << "! icinga2.conf not found, therefore skipping validation.\n";
}
os << '\n';
PrintZonesConf(os);
os << '\n';
std::cout << "Finished collection";
if (!vm.count("console")) {
os.close();
std::cout << ", see " << path;
}
std::cout << std::endl;
return 0;
}

View File

@ -0,0 +1,45 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2014 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 TROUBLESHOOTCOLLECTCOMMAND_H
#define TROUBLESHOOTCOLLECTCOMMAND_H
#include "cli/clicommand.hpp"
namespace icinga
{
/**
* The "troubleshoot collect" command.
*
* @ingroup cli
*/
class TroubleshootCollectCommand : public CLICommand
{
public:
DECLARE_PTR_TYPEDEFS(TroubleshootCollectCommand);
virtual String GetDescription(void) const;
virtual String GetShortDescription(void) const;
virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const;
virtual void InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const;
static int tail(const String& file, int numLines, std::ostream& os);
};
}
#endif /* TROUBLESHOOTCOLLECTCOMMAND_H */