2015-01-22 12:10:32 +01:00
/*****************************************************************************
* Icinga 2 *
2015-02-20 15:27:10 +01:00
* Copyright ( C ) 2012 - 2015 Icinga Development Team ( https : //www.icinga.org) *
2015-01-22 12:10:32 +01:00
* *
* 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 "base/application.hpp"
2015-02-27 14:09:26 +01:00
# include "base/console.hpp"
2015-02-26 12:40:49 +01:00
# include "base/convert.hpp"
2015-01-22 12:10:32 +01:00
# include "base/json.hpp"
2015-02-26 12:40:49 +01:00
# include "base/netstring.hpp"
2015-01-22 12:10:32 +01:00
# include "base/objectlock.hpp"
2015-02-26 12:40:49 +01:00
# include "base/stdiostream.hpp"
# include "cli/daemonutility.hpp"
# include "cli/featureutility.hpp"
# include "cli/objectlistutility.hpp"
# include "cli/troubleshootcommand.hpp"
# include "cli/variableutility.hpp"
2015-01-22 12:10:32 +01:00
# include "config/configitembuilder.hpp"
2015-02-26 12:40:49 +01:00
# include <boost/algorithm/string/join.hpp>
2015-01-22 12:10:32 +01:00
# include <boost/circular_buffer.hpp>
2015-02-26 12:40:49 +01:00
# include <boost/filesystem.hpp>
2015-01-22 12:10:32 +01:00
# include <boost/foreach.hpp>
2015-02-26 12:40:49 +01:00
2015-01-22 12:10:32 +01:00
# include <fstream>
2015-02-26 12:40:49 +01:00
# include <iostream>
2015-01-22 12:10:32 +01:00
using namespace icinga ;
namespace po = boost : : program_options ;
2015-02-25 09:51:31 +01:00
REGISTER_CLICOMMAND ( " troubleshoot " , TroubleshootCommand ) ;
2015-01-22 12:10:32 +01:00
2015-02-25 09:51:31 +01:00
String TroubleshootCommand : : GetDescription ( void ) const
2015-01-22 12:10:32 +01:00
{
return " Collect logs and other relevant information for troubleshooting purposes. " ;
}
2015-02-25 09:51:31 +01:00
String TroubleshootCommand : : GetShortDescription ( void ) const
2015-01-22 12:10:32 +01:00
{
2015-02-25 15:39:17 +01:00
return " collect information for troubleshooting " ;
2015-01-22 12:10:32 +01:00
}
2015-02-25 09:51:31 +01:00
class TroubleshootCommand : : InfoLog
2015-01-22 12:10:32 +01:00
{
2015-02-19 17:12:32 +01:00
public :
InfoLog ( const String & path , const bool cons )
{
2015-02-20 14:14:34 +01:00
m_Console = cons ;
2015-02-27 14:09:26 +01:00
m_ConsoleType = Console_Dumb ;
if ( m_Console ) {
2015-02-20 14:14:34 +01:00
m_Stream = new std : : ostream ( std : : cout . rdbuf ( ) ) ;
2015-02-27 14:09:26 +01:00
# ifndef _WIN32
m_ConsoleType = Console_VT100 ;
# else /*_WIN32*/
m_ConsoleType = Console_Windows ;
# endif /*_WIN32*/
}
2015-02-20 14:14:34 +01:00
else {
2015-02-20 13:40:08 +01:00
std : : ofstream * ofs = new std : : ofstream ( ) ;
ofs - > open ( path . CStr ( ) , std : : ios : : out | std : : ios : : trunc ) ;
2015-02-20 14:14:34 +01:00
m_Stream = ofs ;
2015-02-19 17:12:32 +01:00
}
2015-02-20 14:14:34 +01:00
}
2015-02-19 17:12:32 +01:00
2015-02-25 13:49:41 +01:00
~ InfoLog ( void )
{
delete m_Stream ;
}
2015-02-25 11:08:05 +01:00
2015-02-27 14:09:26 +01:00
void WriteLine ( const LogSeverity sev , const int colour , const String & str )
2015-02-19 17:12:32 +01:00
{
2015-02-20 14:14:34 +01:00
if ( ! m_Console )
2015-02-19 17:12:32 +01:00
Log ( sev , " troubleshoot " , str ) ;
2015-02-27 14:09:26 +01:00
if ( sev = = LogWarning ) {
2015-02-20 16:13:25 +01:00
* m_Stream
2015-02-27 14:09:26 +01:00
< < ' \n ' < < ConsoleColorTag ( Console_ForegroundYellow , m_ConsoleType ) < < std : : string ( 24 , ' # ' ) < < ' \n '
< < ConsoleColorTag ( Console_Normal , m_ConsoleType ) < < str
< < ConsoleColorTag ( Console_ForegroundYellow , m_ConsoleType ) < < std : : string ( 24 , ' # ' ) < < " \n \n "
< < ConsoleColorTag ( Console_Normal , m_ConsoleType ) ;
} else if ( sev = = LogCritical ) {
* m_Stream
< < ' \n ' < < ConsoleColorTag ( Console_ForegroundRed , m_ConsoleType ) < < std : : string ( 24 , ' # ' ) < < ' \n '
< < ConsoleColorTag ( Console_Normal , m_ConsoleType ) < < str
< < ConsoleColorTag ( Console_ForegroundRed , m_ConsoleType ) < < std : : string ( 24 , ' # ' ) < < " \n \n "
< < ConsoleColorTag ( Console_Normal , m_ConsoleType ) ;
2015-02-19 17:12:32 +01:00
} else
2015-02-20 16:13:25 +01:00
* m_Stream
2015-02-27 14:09:26 +01:00
< < ConsoleColorTag ( colour , m_ConsoleType ) < < str
< < ConsoleColorTag ( Console_Normal , m_ConsoleType ) ;
2015-01-22 12:10:32 +01:00
}
2015-02-20 14:14:34 +01:00
bool GetStreamHealth ( void ) const
2015-02-19 17:12:32 +01:00
{
2015-02-20 16:59:10 +01:00
return m_Stream - > good ( ) ;
2015-02-19 17:12:32 +01:00
}
2015-02-20 14:14:34 +01:00
private :
bool m_Console ;
2015-02-27 14:09:26 +01:00
ConsoleType m_ConsoleType ;
2015-02-20 14:14:34 +01:00
std : : ostream * m_Stream ;
2015-02-19 17:12:32 +01:00
} ;
2015-02-25 09:51:31 +01:00
class TroubleshootCommand : : InfoLogLine
2015-01-22 12:10:32 +01:00
{
2015-02-19 17:12:32 +01:00
public :
2015-02-27 14:09:26 +01:00
InfoLogLine ( InfoLog & log , int col = Console_Normal , LogSeverity sev = LogInformation )
: m_Log ( log ) , m_colour ( col ) , m_Sev ( sev ) { }
2015-01-22 12:10:32 +01:00
2015-02-25 11:08:05 +01:00
~ InfoLogLine ( void )
2015-02-19 17:12:32 +01:00
{
2015-02-27 14:09:26 +01:00
m_Log . WriteLine ( m_Sev , m_colour , m_String . str ( ) ) ;
2015-01-22 12:10:32 +01:00
}
2015-02-19 17:12:32 +01:00
template < typename T >
InfoLogLine & operator < < ( const T & info )
{
2015-02-20 14:14:34 +01:00
m_String < < info ;
2015-02-19 17:12:32 +01:00
return * this ;
2015-01-22 12:10:32 +01:00
}
2015-02-19 17:12:32 +01:00
private :
2015-02-20 14:14:34 +01:00
std : : ostringstream m_String ;
InfoLog & m_Log ;
LogSeverity m_Sev ;
2015-02-27 14:09:26 +01:00
int m_colour ;
2015-02-19 17:12:32 +01:00
} ;
2015-02-25 09:51:31 +01:00
bool TroubleshootCommand : : GeneralInfo ( InfoLog & log , const boost : : program_options : : variables_map & vm )
2015-02-19 17:12:32 +01:00
{
2015-02-27 14:09:26 +01:00
InfoLogLine ( log , Console_ForegroundBlue )
< < ' \n ' < < std : : string ( 14 , ' = ' ) < < " GENERAL INFORMATION " < < std : : string ( 14 , ' = ' ) < < " \n \n " ;
2015-02-19 17:12:32 +01:00
2015-02-20 15:27:10 +01:00
//Application::DisplayInfoMessage() but formatted
2015-02-19 17:12:32 +01:00
InfoLogLine ( log )
2015-02-20 15:27:10 +01:00
< < " \t Application version: " < < Application : : GetVersion ( ) < < ' \n '
< < " \t Installation root: " < < Application : : GetPrefixDir ( ) < < ' \n '
< < " \t Sysconf directory: " < < Application : : GetSysconfDir ( ) < < ' \n '
< < " \t Run directory: " < < Application : : GetRunDir ( ) < < ' \n '
< < " \t Local state directory: " < < Application : : GetLocalStateDir ( ) < < ' \n '
< < " \t Package data directory: " < < Application : : GetPkgDataDir ( ) < < ' \n '
< < " \t State path: " < < Application : : GetStatePath ( ) < < ' \n '
< < " \t Objects path: " < < Application : : GetObjectsPath ( ) < < ' \n '
< < " \t Vars path: " < < Application : : GetVarsPath ( ) < < ' \n '
< < " \t PID path: " < < Application : : GetPidPath ( ) < < ' \n '
2015-02-27 14:09:26 +01:00
< < " \t Application type: " < < Application : : GetApplicationType ( ) < < " \n \n " ;
2015-02-19 17:12:32 +01:00
return true ;
}
2015-02-25 09:51:31 +01:00
bool TroubleshootCommand : : FeatureInfo ( InfoLog & log , const boost : : program_options : : variables_map & vm )
2015-02-19 17:12:32 +01:00
{
2015-02-25 09:51:31 +01:00
TroubleshootCommand : : CheckFeatures ( log ) ;
2015-02-25 11:08:05 +01:00
//TODO Check whether active features are operational.
2015-02-19 17:12:32 +01:00
return true ;
}
2015-02-26 12:40:49 +01:00
bool TroubleshootCommand : : ObjectInfo ( InfoLog & log , const boost : : program_options : : variables_map & vm , Dictionary : : Ptr & logs , const String & path )
2015-02-19 17:12:32 +01:00
{
2015-02-27 14:09:26 +01:00
InfoLogLine ( log , Console_ForegroundBlue )
< < ' \n ' < < std : : string ( 14 , ' = ' ) < < " OBJECT INFORMATION " < < std : : string ( 14 , ' = ' ) < < " \n \n " ;
2015-02-19 17:12:32 +01:00
String objectfile = Application : : GetObjectsPath ( ) ;
std : : set < String > configs ;
if ( ! Utility : : PathExists ( objectfile ) ) {
2015-02-27 14:09:26 +01:00
InfoLogLine ( log , 0 , LogCritical )
2015-02-20 16:13:25 +01:00
< < " Cannot open object file ' " < < objectfile < < " '. \n "
2015-02-27 14:09:26 +01:00
< < " FAILED: This probably means you have a fault configuration. \n " ;
2015-02-19 17:12:32 +01:00
return false ;
2015-02-26 12:40:49 +01:00
} else {
InfoLog * OFile = NULL ;
if ( vm . count ( " include-objects " ) ) {
2015-02-26 13:29:55 +01:00
OFile = new InfoLog ( path + " -objects " , false ) ;
2015-02-26 12:40:49 +01:00
if ( ! OFile - > GetStreamHealth ( ) ) {
2015-02-27 14:09:26 +01:00
InfoLogLine ( log , 0 , LogWarning )
< < " Failed to open Object-write-stream, not printing objects \n \n " ;
2015-02-26 12:40:49 +01:00
OFile = NULL ;
} else
InfoLogLine ( log )
2015-02-27 14:09:26 +01:00
< < " Printing all objects to " < < path + " -objects \n " ;
2015-02-26 12:40:49 +01:00
}
CheckObjectFile ( objectfile , log , OFile , logs , configs ) ;
if ( OFile ! = NULL )
delete OFile ;
}
2015-02-19 17:12:32 +01:00
2015-02-26 12:40:49 +01:00
if ( vm . count ( " include-vars " ) ) {
if ( PrintVarsFile ( path ) )
InfoLogLine ( log )
2015-02-27 14:09:26 +01:00
< < " Successfully printed all variables to " < < path + " -vars \n " ;
2015-02-26 12:40:49 +01:00
else
2015-02-27 14:09:26 +01:00
InfoLogLine ( log , 0 , LogWarning )
< < " Failed to prin vars to " < < path + " -vars \n " ;
2015-02-26 12:40:49 +01:00
}
2015-02-19 17:12:32 +01:00
return true ;
}
2015-02-25 09:51:31 +01:00
bool TroubleshootCommand : : ReportInfo ( InfoLog & log , const boost : : program_options : : variables_map & vm , Dictionary : : Ptr & logs )
2015-02-19 17:12:32 +01:00
{
2015-02-27 14:09:26 +01:00
InfoLogLine ( log , Console_ForegroundBlue )
< < ' \n ' < < std : : string ( 14 , ' = ' ) < < " LOGS AND CRASH REPORTS " < < std : : string ( 14 , ' = ' ) < < " \n \n " ;
2015-02-19 17:12:32 +01:00
PrintLoggers ( log , logs ) ;
PrintCrashReports ( log ) ;
return true ;
}
2015-02-25 09:51:31 +01:00
bool TroubleshootCommand : : ConfigInfo ( InfoLog & log , const boost : : program_options : : variables_map & vm )
2015-02-19 17:12:32 +01:00
{
2015-02-27 14:09:26 +01:00
InfoLogLine ( log , Console_ForegroundBlue )
< < ' \n ' < < std : : string ( 14 , ' = ' ) < < " CONFIGURATION FILES " < < std : : string ( 14 , ' = ' ) < < " \n \n " ;
2015-02-20 16:13:25 +01:00
InfoLogLine ( log )
2015-02-27 14:09:26 +01:00
< < " A collection of important configuration files follows, please make sure to remove any sensitive data such as credentials, internal company names, etc \n " ;
2015-02-19 17:12:32 +01:00
if ( ! PrintConf ( log , Application : : GetSysconfDir ( ) + " /icinga2/icinga2.conf " ) ) {
2015-02-27 14:09:26 +01:00
InfoLogLine ( log , 0 , LogWarning )
2015-02-20 15:27:10 +01:00
< < " icinga2.conf not found, therefore skipping validation. \n "
< < " If you are using an icinga2.conf somewhere but the default path please validate it via 'icinga2 daemon -C -c \" path \t o/icinga2.conf \" ' \n "
2015-02-27 14:09:26 +01:00
< < " and provide it with your support request. \n " ;
2015-01-22 12:10:32 +01:00
}
2015-02-19 17:12:32 +01:00
if ( ! PrintConf ( log , Application : : GetSysconfDir ( ) + " /icinga2/zones.conf " ) ) {
2015-02-27 14:09:26 +01:00
InfoLogLine ( log , 0 , LogWarning )
2015-02-20 15:27:10 +01:00
< < " zones.conf not found. \n "
2015-02-27 14:09:26 +01:00
< < " If you are using a zones.conf somewhere but the default path please provide it with your support request \n " ;
2015-01-22 12:10:32 +01:00
}
2015-02-19 17:12:32 +01:00
return true ;
2015-01-22 12:10:32 +01:00
}
/*Print the last *numLines* of *file* to *os* */
2015-02-25 09:51:31 +01:00
int TroubleshootCommand : : Tail ( const String & file , int numLines , InfoLog & log )
2015-01-22 12:10:32 +01:00
{
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 ;
2015-02-25 09:51:31 +01:00
2015-02-27 14:09:26 +01:00
InfoLogLine ( log , Console_ForegroundCyan )
< < " [begin: ' " < < file < < " ' line: " < < lines - numLines < < " ] \n " ;
2015-01-22 12:10:32 +01:00
2015-02-20 15:27:10 +01:00
for ( int k = 0 ; k < numLines ; k + + ) {
2015-02-27 14:09:26 +01:00
InfoLogLine ( log , Console_ForegroundCyan )
< < " # " ;
InfoLogLine ( log )
< < ringBuf [ k ] < < ' \n ' ;
2015-02-20 15:27:10 +01:00
}
2015-01-22 12:10:32 +01:00
text . close ( ) ;
2015-02-20 15:27:10 +01:00
2015-02-27 14:09:26 +01:00
InfoLogLine ( log , Console_ForegroundCyan )
< < " [end: ' " < < file < < " ' line: " < < lines < < " ] \n \n " ;
2015-02-20 15:27:10 +01:00
2015-01-22 12:10:32 +01:00
return numLines ;
}
2015-02-25 09:51:31 +01:00
bool TroubleshootCommand : : CheckFeatures ( InfoLog & log )
2015-01-22 12:10:32 +01:00
{
2015-02-19 17:12:32 +01:00
Dictionary : : Ptr features = new Dictionary ;
std : : vector < String > disabled_features ;
std : : vector < String > enabled_features ;
2015-01-22 12:10:32 +01:00
2015-02-20 15:27:10 +01:00
if ( ! FeatureUtility : : GetFeatures ( disabled_features , true ) | |
! FeatureUtility : : GetFeatures ( enabled_features , false ) ) {
2015-02-27 14:09:26 +01:00
InfoLogLine ( log , 0 , LogCritical )
2015-02-20 15:27:10 +01:00
< < " Failed to collect enabled and/or disabled features. Check \n "
< < FeatureUtility : : GetFeaturesAvailablePath ( ) < < ' \n '
2015-02-27 14:09:26 +01:00
< < FeatureUtility : : GetFeaturesEnabledPath ( ) < < ' \n ' ;
2015-01-22 12:10:32 +01:00
return false ;
}
2015-02-19 17:12:32 +01:00
BOOST_FOREACH ( const String feature , disabled_features )
features - > Set ( feature , false ) ;
BOOST_FOREACH ( const String feature , enabled_features )
features - > Set ( feature , true ) ;
2015-02-20 15:27:10 +01:00
InfoLogLine ( log )
2015-02-27 14:09:26 +01:00
< < " Enabled features: \n " ;
InfoLogLine ( log , Console_ForegroundGreen )
< < ' \t ' < < boost : : algorithm : : join ( enabled_features , " " ) < < ' \n ' ;
InfoLogLine ( log )
< < " Disabled features: \n " ;
InfoLogLine ( log , Console_ForegroundRed )
< < ' \t ' < < boost : : algorithm : : join ( disabled_features , " " ) < < ' \n ' ;
2015-02-19 17:12:32 +01:00
if ( ! features - > Get ( " checker " ) . ToBool ( ) )
2015-02-27 14:09:26 +01:00
InfoLogLine ( log , 0 , LogWarning )
< < " checker is disabled, no checks can be run from this instance \n " ;
2015-02-19 17:12:32 +01:00
if ( ! features - > Get ( " mainlog " ) . ToBool ( ) )
2015-02-27 14:09:26 +01:00
InfoLogLine ( log , 0 , LogWarning )
< < " mainlog is disabled, please activate it and rerun icinga2 \n " ;
2015-02-19 17:12:32 +01:00
if ( ! features - > Get ( " debuglog " ) . ToBool ( ) )
2015-02-27 14:09:26 +01:00
InfoLogLine ( log , 0 , LogWarning )
< < " debuglog is disabled, please activate it and rerun icinga2 \n " ;
2015-02-20 15:27:10 +01:00
2015-01-22 12:10:32 +01:00
return true ;
}
2015-02-25 09:51:31 +01:00
void TroubleshootCommand : : GetLatestReport ( const String & filename , time_t & bestTimestamp , String & bestFilename )
2015-02-19 17:12:32 +01:00
{
# 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 ;
}
}
2015-02-25 09:51:31 +01:00
bool TroubleshootCommand : : PrintCrashReports ( InfoLog & log )
2015-01-22 12:10:32 +01:00
{
2015-02-19 17:12:32 +01:00
String spath = Application : : GetLocalStateDir ( ) + " /log/icinga2/crash/report.* " ;
time_t bestTimestamp = 0 ;
String bestFilename ;
2015-01-22 12:10:32 +01:00
2015-02-19 17:12:32 +01:00
try {
2015-02-20 15:27:10 +01:00
Utility : : Glob ( spath , boost : : bind ( & GetLatestReport , _1 , boost : : ref ( bestTimestamp ) ,
boost : : ref ( bestFilename ) ) , GlobFile ) ;
2015-02-19 17:12:32 +01:00
}
# 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
2015-02-27 14:09:26 +01:00
InfoLogLine ( log , 0 , LogWarning )
2015-02-25 13:49:41 +01:00
< < Application : : GetLocalStateDir ( ) < < " /log/icinga2/crash/ does not exist \n " ;
2015-02-20 15:27:10 +01:00
2015-02-19 17:12:32 +01:00
return false ;
}
2015-02-20 15:27:10 +01:00
}
2015-02-27 14:09:26 +01:00
InfoLogLine ( log , 0 , LogWarning )
< < " Error printing crash reports \n " ;
2015-02-20 15:27:10 +01:00
2015-02-19 17:12:32 +01:00
return false ;
}
# else
catch ( . . . ) {
2015-02-27 14:09:26 +01:00
InfoLogLine ( log , 0 , LogWarning ) < < " Error printing crash reports. \n "
2015-02-25 13:49:41 +01:00
< < " Does " < < Application : : GetLocalStateDir ( ) < < " /log/icinga2/crash/ exist? \n " ;
2015-02-20 15:27:10 +01:00
return false ;
2015-02-19 17:12:32 +01:00
}
# endif /*_WIN32*/
if ( ! bestTimestamp )
2015-02-27 14:09:26 +01:00
InfoLogLine ( log , Console_ForegroundYellow )
< < " No crash logs found in " < < Application : : GetLocalStateDir ( ) . CStr ( ) < < " /log/icinga2/crash/ \n \n " ;
2015-02-19 17:12:32 +01:00
else {
2015-02-20 15:27:10 +01:00
InfoLogLine ( log )
2015-02-27 14:09:26 +01:00
< < " Latest crash report is from " < < Utility : : FormatDateTime ( " %Y-%m-%d %H:%M:%S " , Utility : : GetTime ( ) ) < < ' \n '
< < " File: " < < bestFilename < < ' \n ' ;
2015-02-20 14:14:34 +01:00
Tail ( bestFilename , 20 , log ) ;
2015-02-25 13:49:41 +01:00
InfoLogLine ( log )
2015-02-27 14:09:26 +01:00
< < ' \n ' ;
2015-02-19 17:12:32 +01:00
}
2015-02-20 15:27:10 +01:00
2015-02-19 17:12:32 +01:00
return true ;
}
2015-02-25 09:51:31 +01:00
bool TroubleshootCommand : : PrintConf ( InfoLog & log , const String & path )
2015-02-19 17:12:32 +01:00
{
2015-01-22 12:10:32 +01:00
std : : ifstream text ;
text . open ( path . CStr ( ) , std : : ifstream : : in ) ;
2015-02-19 17:12:32 +01:00
if ( ! text . is_open ( ) )
2015-01-22 12:10:32 +01:00
return false ;
2015-02-19 17:12:32 +01:00
2015-01-22 12:10:32 +01:00
std : : string line ;
2015-02-27 14:09:26 +01:00
InfoLogLine ( log , Console_ForegroundCyan )
< < " [begin: ' " < < path < < " '] \n " ;
2015-02-20 15:27:10 +01:00
2015-01-22 12:10:32 +01:00
while ( std : : getline ( text , line ) ) {
2015-02-27 14:09:26 +01:00
InfoLogLine ( log , Console_ForegroundCyan )
< < " # " ;
InfoLogLine ( log )
< < line < < ' \n ' ;
2015-01-22 12:10:32 +01:00
}
2015-02-20 15:27:10 +01:00
2015-02-27 14:09:26 +01:00
InfoLogLine ( log , Console_ForegroundCyan )
< < " [end: ' " < < path < < " '] \n " ;
2015-02-20 15:27:10 +01:00
2015-01-22 12:10:32 +01:00
return true ;
}
2015-02-25 09:51:31 +01:00
bool TroubleshootCommand : : CheckConfig ( void )
2015-01-22 12:10:32 +01:00
{
/* Not loading the icinga library would make config validation fail.
2015-02-20 15:27:10 +01:00
* ( Depending on the configuration and the speed of your machine . )
2015-02-19 17:12:32 +01:00
*/
2015-01-22 12:10:32 +01:00
Utility : : LoadExtensionLibrary ( " icinga " ) ;
std : : vector < std : : string > configs ;
configs . push_back ( Application : : GetSysconfDir ( ) + " /icinga2/icinga2.conf " ) ;
2015-02-20 15:27:10 +01:00
2015-02-19 17:12:32 +01:00
return DaemonUtility : : ValidateConfigFiles ( configs , Application : : GetObjectsPath ( ) ) ;
2015-01-22 12:10:32 +01:00
}
2015-02-25 15:39:17 +01:00
//print is supposed allow the user to print the object file
2015-02-26 12:40:49 +01:00
void TroubleshootCommand : : CheckObjectFile ( const String & objectfile , InfoLog & log , InfoLog * OFile ,
2015-02-20 15:27:10 +01:00
Dictionary : : Ptr & logs , std : : set < String > & configs )
2015-01-22 12:10:32 +01:00
{
2015-02-20 15:27:10 +01:00
InfoLogLine ( log )
2015-02-27 14:09:26 +01:00
< < " Checking object file from " < < objectfile < < ' \n ' ;
2015-01-22 12:10:32 +01:00
std : : fstream fp ;
fp . open ( objectfile . CStr ( ) , std : : ios_base : : in ) ;
if ( ! fp . is_open ( ) ) {
2015-02-27 14:09:26 +01:00
InfoLogLine ( log , 0 , LogWarning )
< < " Could not open object file. \n " ;
2015-01-22 12:10:32 +01:00
return ;
}
2015-02-20 15:27:10 +01:00
2015-01-22 12:10:32 +01:00
StdioStream : : Ptr sfp = new StdioStream ( & fp , false ) ;
2015-02-19 17:12:32 +01:00
String : : SizeType typeL = 0 , countTotal = 0 ;
2015-02-15 14:42:53 +01:00
2015-01-22 12:10:32 +01:00
String message ;
StreamReadContext src ;
2015-02-19 17:12:32 +01:00
StreamReadStatus srs ;
std : : map < String , int > type_count ;
bool first = true ;
2015-02-20 15:27:10 +01:00
2015-02-26 12:40:49 +01:00
std : : stringstream sStream ;
2015-02-19 17:12:32 +01:00
while ( ( srs = NetString : : ReadStringFromStream ( sfp , & message , src ) ) ! = StatusEof ) {
2015-02-15 14:42:53 +01:00
if ( srs ! = StatusNewItem )
continue ;
2015-02-25 15:39:17 +01:00
2015-02-20 09:55:07 +01:00
ObjectListUtility : : PrintObject ( sStream , first , message , type_count , " " , " " ) ;
2015-02-26 12:40:49 +01:00
if ( OFile ! = NULL ) {
InfoLogLine ( * OFile )
< < sStream . str ( ) ;
sStream . flush ( ) ;
}
2015-02-20 15:27:10 +01:00
2015-01-22 12:10:32 +01:00
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 + + ;
Array : : Ptr debug_info = object - > Get ( " debug_info " ) ;
2015-02-20 15:27:10 +01:00
if ( debug_info )
2015-02-19 17:12:32 +01:00
configs . insert ( debug_info - > Get ( 0 ) ) ;
2015-01-22 12:10:32 +01:00
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 " ) )
2015-02-19 17:12:32 +01:00
logs - > Set ( name , kv . second ) ;
2015-01-22 12:10:32 +01:00
}
}
}
if ( ! countTotal ) {
2015-02-27 14:09:26 +01:00
InfoLogLine ( log , 0 , LogCritical )
< < " No objects found in objectfile. \n " ;
2015-01-22 12:10:32 +01:00
return ;
}
//Print objects with count
2015-02-20 15:27:10 +01:00
InfoLogLine ( log )
2015-02-25 13:49:41 +01:00
< < " Found the " < < countTotal < < " objects: \n "
2015-02-27 14:09:26 +01:00
< < " Type " < < std : : string ( typeL - 4 , ' ' ) < < " : Count \n " ;
2015-02-25 13:49:41 +01:00
2015-02-19 17:12:32 +01:00
BOOST_FOREACH ( const Dictionary : : Pair & kv , type_count ) {
2015-02-20 15:27:10 +01:00
InfoLogLine ( log )
2015-02-27 14:09:26 +01:00
< < " " < < kv . first < < std : : string ( typeL - kv . first . GetLength ( ) , ' ' )
< < " : " < < kv . second < < ' \n ' ;
2015-02-19 17:12:32 +01:00
}
2015-02-25 13:49:41 +01:00
InfoLogLine ( log )
2015-02-27 14:09:26 +01:00
< < ' \n ' ;
2015-02-25 13:49:41 +01:00
TroubleshootCommand : : PrintObjectOrigin ( log , configs ) ;
2015-02-19 17:12:32 +01:00
}
2015-01-22 12:10:32 +01:00
2015-02-26 12:40:49 +01:00
bool TroubleshootCommand : : PrintVarsFile ( const String & path ) {
std : : ofstream * ofs = new std : : ofstream ( ) ;
ofs - > open ( ( path + " -vars " ) . CStr ( ) , std : : ios : : out | std : : ios : : trunc ) ;
if ( ! ofs - > is_open ( ) )
return false ;
else
VariableUtility : : PrintVariables ( * ofs ) ;
ofs - > close ( ) ;
return true ;
}
2015-02-25 09:51:31 +01:00
void TroubleshootCommand : : PrintLoggers ( InfoLog & log , Dictionary : : Ptr & logs )
2015-02-19 17:12:32 +01:00
{
if ( ! logs - > GetLength ( ) ) {
2015-02-27 14:09:26 +01:00
InfoLogLine ( log , 0 , LogWarning )
2015-02-25 13:49:41 +01:00
< < " No loggers found, check whether you enabled any logging features \n " ;
2015-01-22 12:10:32 +01:00
} else {
2015-02-20 15:27:10 +01:00
InfoLogLine ( log )
2015-02-27 14:09:26 +01:00
< < " Getting the last 20 lines of " < < logs - > GetLength ( ) < < " FileLogger objects. \n " ;
2015-02-20 15:27:10 +01:00
2015-02-19 17:12:32 +01:00
ObjectLock ulock ( logs ) ;
2015-02-20 15:27:10 +01:00
BOOST_FOREACH ( const Dictionary : : Pair & kv , logs ) {
InfoLogLine ( log )
2015-02-27 14:09:26 +01:00
< < " Logger " < < kv . first < < " at path: " < < kv . second < < ' \n ' ;
2015-02-20 15:27:10 +01:00
if ( ! Tail ( kv . second , 20 , log ) ) {
2015-02-27 14:09:26 +01:00
InfoLogLine ( log , 0 , LogWarning )
2015-02-25 13:49:41 +01:00
< < kv . second < < " either does not exist or is empty \n " ;
2015-02-20 15:27:10 +01:00
}
2015-01-22 12:10:32 +01:00
}
}
}
2015-02-25 13:49:41 +01:00
void TroubleshootCommand : : PrintObjectOrigin ( InfoLog & log , const std : : set < String > & configSet )
2015-02-19 17:12:32 +01:00
{
2015-02-20 15:27:10 +01:00
InfoLogLine ( log )
2015-02-27 14:09:26 +01:00
< < " The objects origins are: \n " ;
2015-02-20 15:27:10 +01:00
for ( std : : set < String > : : iterator it = configSet . begin ( ) ; it ! = configSet . end ( ) ; it + + ) {
InfoLogLine ( log )
2015-02-27 14:09:26 +01:00
< < " " < < * it < < ' \n ' ;
2015-02-20 15:27:10 +01:00
}
2015-02-19 17:12:32 +01:00
}
2015-02-25 09:51:31 +01:00
void TroubleshootCommand : : InitParameters ( boost : : program_options : : options_description & visibleDesc ,
2015-02-20 15:27:10 +01:00
boost : : program_options : : options_description & hiddenDesc ) const
2015-01-22 12:10:32 +01:00
{
visibleDesc . add_options ( )
( " console,c " , " print to console instead of file " )
2015-02-19 17:12:32 +01:00
( " output,o " , boost : : program_options : : value < std : : string > ( ) , " path to output file " )
2015-02-26 12:40:49 +01:00
( " include-objects " , " Print the whole objectfile (like `object list`) " )
( " include-vars " , " Print all Variables (like `variable list`) " )
2015-01-22 12:10:32 +01:00
;
}
2015-02-25 09:51:31 +01:00
int TroubleshootCommand : : Run ( const boost : : program_options : : variables_map & vm , const std : : vector < std : : string > & ap ) const
2015-01-22 12:10:32 +01:00
{
2015-02-26 13:29:55 +01:00
# ifdef _WIN32 //Dislikes ':' in filenames
String path = Application : : GetLocalStateDir ( ) + " /log/icinga2/troubleshooting- "
+ Utility : : FormatDateTime ( " %Y-%m-%d_%H-%M-%S " , Utility : : GetTime ( ) ) + " .log " ;
# else
String path = Application : : GetLocalStateDir ( ) + " /log/icinga2/troubleshooting- "
+ Utility : : FormatDateTime ( " %Y-%m-%d_%H:%M:%S " , Utility : : GetTime ( ) ) + " .log " ;
# endif /*_WIN32*/
2015-02-19 17:12:32 +01:00
InfoLog * log ;
Logger : : SetConsoleLogSeverity ( LogWarning ) ;
2015-02-26 13:29:55 +01:00
if ( vm . count ( " output " ) )
path = vm [ " output " ] . as < std : : string > ( ) ;
2015-01-22 12:10:32 +01:00
if ( vm . count ( " console " ) ) {
2015-02-19 17:12:32 +01:00
log = new InfoLog ( " " , true ) ;
2015-01-22 12:10:32 +01:00
} else {
2015-02-19 17:12:32 +01:00
log = new InfoLog ( path , false ) ;
if ( ! log - > GetStreamHealth ( ) ) {
2015-01-22 12:10:32 +01:00
Log ( LogCritical , " troubleshoot " , " Failed to open file to write: " + path ) ;
return 3 ;
}
2015-02-20 15:27:10 +01:00
}
2015-02-26 12:40:49 +01:00
2015-01-22 12:10:32 +01:00
String appName = Utility : : BaseName ( Application : : GetArgV ( ) [ 0 ] ) ;
2015-02-19 17:12:32 +01:00
double goTime = Utility : : GetTime ( ) ;
2015-01-22 12:10:32 +01:00
2015-02-20 16:13:25 +01:00
InfoLogLine ( * log )
< < appName < < " -- Troubleshooting help: \n "
< < " Should you run into problems with Icinga please add this file to your help request \n "
2015-02-27 14:09:26 +01:00
< < " Began procedure at timestamp " < < Convert : : ToString ( goTime ) < < " \n \n " ;
2015-01-22 12:10:32 +01:00
if ( appName . GetLength ( ) > 3 & & appName . SubStr ( 0 , 3 ) = = " lt- " )
appName = appName . SubStr ( 3 , appName . GetLength ( ) - 3 ) ;
2015-02-19 17:12:32 +01:00
Dictionary : : Ptr logs = new Dictionary ;
2015-01-22 12:10:32 +01:00
2015-02-20 15:27:10 +01:00
if ( ! GeneralInfo ( * log , vm ) | |
! FeatureInfo ( * log , vm ) | |
2015-02-26 12:40:49 +01:00
! ObjectInfo ( * log , vm , logs , path ) | |
2015-02-20 15:27:10 +01:00
! ReportInfo ( * log , vm , logs ) | |
! ConfigInfo ( * log , vm ) ) {
2015-02-27 14:09:26 +01:00
InfoLogLine ( * log , 0 , LogCritical )
< < " Could not recover from critical failure, exiting. \n " ;
2015-02-20 15:27:10 +01:00
2015-02-19 17:12:32 +01:00
delete log ;
return 3 ;
2015-01-22 12:10:32 +01:00
}
2015-02-20 15:27:10 +01:00
2015-02-19 17:12:32 +01:00
double endTime = Utility : : GetTime ( ) ;
2015-02-20 15:27:10 +01:00
InfoLogLine ( * log )
< < " \n Finished collection at timestamp " < < Convert : : ToString ( endTime )
< < " \n Took " < < Convert : : ToString ( endTime - goTime ) < < " seconds \n " ;
2015-01-22 12:10:32 +01:00
if ( ! vm . count ( " console " ) ) {
2015-02-20 16:13:25 +01:00
std : : cout
2015-03-02 09:35:04 +01:00
< < " Finished collection. See ' " < < path < < " ' \n "
2015-02-27 14:09:26 +01:00
< < " Please compress these files with tar or zip before uploading them \n " ;
2015-01-22 12:10:32 +01:00
}
2015-02-19 17:12:32 +01:00
delete log ;
2015-01-22 12:10:32 +01:00
return 0 ;
}