global = true
}
EOF
-
-## Using Multiple Environments <a id="distributed-monitoring-environments"></a>
-
-In some cases it might be useful to run multiple Icinga instance on the same host. Two potential scenarios include:
-
-* running different versions of the same monitoring configuration (e.g. production and testing)
-* running disparate sets of checks for entirely unrelated monitoring environments (e.g. infrastructure and applications)
-
-Icinga supports these scenarios by providing a global variable called `Environment`. It can be set like any other
-global variable, e.g. in the `constants.conf` file or in a file in a global template zone.
-
-The `Environment` variable can also be set using the `--env` command-line option. Setting the variable this way
-has the added effect that Icinga tries to determine if the `Icinga Environments` add-on is available and
-if so uses its CLI commands to determine the configuration and data paths. This command-line option also has the
-effect of changing the listener port for cluster feature to a random port which is made available to the
-`Icinga Environments` add-on through its `config.json` file.
-
-When Icinga establishes a TLS connection to another cluster instance it automatically uses the SNI extension
-to signal which endpoint it is attempting to connect to. On its own this can already be used to position multiple
-Icinga instances behind a load balancer.
-
-SNI example: `icinga2-client1.localdomain`
-
-However, if the `Environment` variable is non-empty Icinga will append the environment name to the SNI hostname
-like this:
-
-SNI example with environment: `icinga2-client1.localdomain:production`
-
-The Icinga Environments add-on provides a reverse proxy which does SNI-based port multiplexing. I.e., it uses a
-single externally-visible TCP port (usually 5665) and forwards connections to one or more Icinga instances
-which are bound to a local TCP port. It does so by inspecting the environment name that is sent as part of the
-SNI extension.
#include "base/context.hpp"
#include "base/console.hpp"
#include "base/process.hpp"
-#include "base/json.hpp"
-#include "base/tcpsocket.hpp"
#include "config.h"
#include <boost/program_options.hpp>
#include <boost/algorithm/string/split.hpp>
# include <Lmcons.h>
# include <Shellapi.h>
# include <tchar.h>
-
-# define popen _popen
-# define pclose _pclose
#endif /* _WIN32 */
using namespace icinga;
Application::DeclareConcurrency(std::thread::hardware_concurrency());
Application::DeclareMaxConcurrentChecks(Application::GetDefaultMaxConcurrentChecks());
+ ScriptGlobal::Set("Environment", "production");
+
ScriptGlobal::Set("AttachDebugger", false);
ScriptGlobal::Set("PlatformKernel", Utility::GetPlatformKernel());
("include,I", po::value<std::vector<std::string> >(), "add include search directory")
("log-level,x", po::value<std::string>(), "specify the log level for the console log.\n"
"The valid value is either debug, notice, information (default), warning, or critical")
- ("script-debugger,X", "whether to enable the script debugger")
- ("env", po::value<std::string>(), "the name of the environment, e.g. \"production\"");
+ ("script-debugger,X", "whether to enable the script debugger");
po::options_description hiddenDesc("Hidden options");
return EXIT_FAILURE;
}
- if (vm.count("env") && vm["env"].as<std::string>() != "") {
- String env = vm["env"].as<std::string>();
- ScriptGlobal::Set("Environment", env);
- } else {
-#ifndef _WIN32
- String cmd = "icinga-envs";
-#else /* _WIN32 */
- String cmd = "\"" + Utility::DirName(Application::GetExePath(argv[0])) + "\\icinga-envs.exe" + "\"";
-#endif /* _WIN32 */
- cmd += " get-default";
-#ifndef _WIN32
- cmd += " 2>/dev/null";
-#else /* _WIN32 */
- cmd += " 2>NUL";
- cmd = "\"" + cmd + "\"";
-#endif /* _WIN32 */
-
- FILE *fp = popen(cmd.CStr(), "r");
- std::stringstream msgbuf;
- while (!ferror(fp) && !feof(fp)) {
- char buf[512];
- size_t num = fread(buf, 1, sizeof(buf), fp);
- msgbuf << std::string(buf, buf + num);
- }
- pclose(fp);
-
- String env = msgbuf.str();
- ScriptGlobal::Set("Environment", env.Trim());
- }
-
- String env = ScriptGlobal::Get("Environment", NULL);
-#ifndef _WIN32
- String cmd = "icinga-envs";
-#else /* _WIN32 */
- String cmd = "\"" + Utility::DirName(Application::GetExePath(argv[0])) + "\\icinga-envs.exe" + "\"";
-#endif /* _WIN32 */
- cmd += " info --json " + Utility::EscapeShellArg(env);
-#ifndef _WIN32
- cmd += " 2>/dev/null";
-#else /* _WIN32 */
- cmd += " 2>NUL";
- cmd = "\"" + cmd + "\"";
-#endif /* _WIN32 */
-
- FILE *fp = popen(cmd.CStr(), "r");
- std::stringstream msgbuf;
- while (!ferror(fp) && !feof(fp)) {
- char buf[512];
- size_t num = fread(buf, 1, sizeof(buf), fp);
- msgbuf << std::string(buf, buf + num);
- }
- pclose(fp);
-
- Dictionary::Ptr envInfo;
- try {
- envInfo = JsonDecode(msgbuf.str());
- } catch (const std::exception&) {}
-
- if (envInfo) {
- Dictionary::Ptr config = envInfo->Get("config");
-
- if (!config) {
- Log(LogCritical, "app")
- << "Invalid JSON returned by icinga-envs: " << msgbuf.str();
- return EXIT_FAILURE;
- }
-
- String configPath = config->Get("config_path");
- String dataPath = config->Get("data_path");
-
- ScriptGlobal::Set("SysconfDir", configPath);
- ScriptGlobal::Set("RunDir", dataPath + "/run");
- ScriptGlobal::Set("LocalStateDir", dataPath);
-
- TcpSocket::Ptr agentListener = new TcpSocket();
- agentListener->Bind("127.0.0.1", "0", AF_INET);
- auto pieces = agentListener->GetClientAddress().Split(":");
- config->Set("port", Convert::ToLong(pieces[pieces.size() - 1]));
-
- Dictionary::Ptr envData = new Dictionary({
- { "listener", agentListener },
- { "config", config },
- { "meta_path", envInfo->Get("env_path") + "/config.json" }
- });
-
- // Make the agent listener available to the ApiListener later on
- ScriptGlobal::Set("EnvironmentInfo", envData);
- } else if (env != "") {
- Log(LogCritical, "app")
- << "No such environment exists: " << env;
- return EXIT_FAILURE;
- }
-
#ifdef _WIN32
char username[UNLEN + 1];
DWORD usernameLen = UNLEN + 1;
<Binary Id="icinga2_installer" SourceFile="$<TARGET_FILE:icinga-installer>" />
<InstallExecuteSequence>
- <Custom Action="XtraUpgradeNSIS" After="InstallInitialize">$CM_CP_sbin.icinga2_installer.exe>2 AND NOT SUPPRESS_XTRA</Custom>
- <Custom Action="XtraInstall" Before="InstallFinalize">$CM_CP_sbin.icinga2_installer.exe>2 AND NOT SUPPRESS_XTRA</Custom>
- <Custom Action="XtraUninstall" Before="RemoveExistingProducts">$CM_CP_sbin.icinga2_installer.exe=2 AND NOT SUPPRESS_XTRA</Custom>
+ <Custom Action="XtraUpgradeNSIS" After="InstallInitialize">$CM_CP_sbin.icinga2_installer.exe>2</Custom>
+ <Custom Action="XtraInstall" Before="InstallFinalize">$CM_CP_sbin.icinga2_installer.exe>2</Custom>
+ <Custom Action="XtraUninstall" Before="RemoveExistingProducts">$CM_CP_sbin.icinga2_installer.exe=2</Custom>
</InstallExecuteSequence>
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="Run Icinga 2 setup wizard" />
return -1;
}
-void ApiSetupCommand::InitParameters(boost::program_options::options_description& visibleDesc,
- boost::program_options::options_description& hiddenDesc) const
-{
- visibleDesc.add_options()
- ("quiet,q", "be less verbose")
- ;
-}
-
/**
* The entry point for the "api setup" CLI command.
*
*/
int ApiSetupCommand::Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const
{
- if (vm.count("quiet"))
- Logger::SetConsoleLogSeverity(LogWarning);
-
String cn = VariableUtility::GetVariable("NodeName");
if (cn.IsEmpty())
cn = Utility::GetFQDN();
- if (!ApiSetupUtility::SetupMaster(cn, true, vm.count("quiet")))
+ if (!ApiSetupUtility::SetupMaster(cn, true))
return 1;
return 0;
String GetDescription() const override;
String GetShortDescription() const override;
int GetMaxArguments() const override;
- void InitParameters(boost::program_options::options_description& visibleDesc,
- boost::program_options::options_description& hiddenDesc) const override;
int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const override;
ImpersonationLevel GetImpersonationLevel() const override;
};
return ApiSetupUtility::GetConfdPath() + "/api-users.conf";
}
-bool ApiSetupUtility::SetupMaster(const String& cn, bool prompt_restart, bool quiet)
+bool ApiSetupUtility::SetupMaster(const String& cn, bool prompt_restart)
{
if (!SetupMasterCertificates(cn))
return false;
if (!SetupMasterApiUser())
return false;
- if (!SetupMasterEnableApi(quiet))
+ if (!SetupMasterEnableApi())
return false;
if (!SetupMasterUpdateConstants(cn))
return false;
- if (prompt_restart && !quiet) {
+ if (prompt_restart) {
std::cout << "Done.\n\n";
std::cout << "Now restart your Icinga 2 daemon to finish the installation!\n\n";
}
return true;
}
-bool ApiSetupUtility::SetupMasterEnableApi(bool quiet)
+bool ApiSetupUtility::SetupMasterEnableApi()
{
Log(LogInformation, "cli", "Enabling the 'api' feature.");
- FeatureUtility::EnableFeatures({ "api" }, quiet);
+ FeatureUtility::EnableFeatures({ "api" });
return true;
}
class ApiSetupUtility
{
public:
- static bool SetupMaster(const String& cn, bool prompt_restart = false, bool quiet = false);
+ static bool SetupMaster(const String& cn, bool prompt_restart = false);
static bool SetupMasterCertificates(const String& cn);
static bool SetupMasterApiUser();
- static bool SetupMasterEnableApi(bool quiet = false);
+ static bool SetupMasterEnableApi();
static bool SetupMasterUpdateConstants(const String& cn);
static String GetConfdPath();
return suggestions;
}
-int FeatureUtility::EnableFeatures(const std::vector<std::string>& features, bool quiet)
+int FeatureUtility::EnableFeatures(const std::vector<std::string>& features)
{
String features_available_dir = GetFeaturesAvailablePath();
String features_enabled_dir = GetFeaturesEnabledPath();
continue;
}
- if (!quiet)
- std::cout << "Enabling feature " << ConsoleColorTag(Console_ForegroundMagenta | Console_Bold) << feature
- << ConsoleColorTag(Console_Normal) << ". Make sure to restart Icinga 2 for these changes to take effect.\n";
+ std::cout << "Enabling feature " << ConsoleColorTag(Console_ForegroundMagenta | Console_Bold) << feature
+ << ConsoleColorTag(Console_Normal) << ". Make sure to restart Icinga 2 for these changes to take effect.\n";
#ifndef _WIN32
String relativeSource = "../features-available/" + feature + ".conf";
return 0;
}
-int FeatureUtility::DisableFeatures(const std::vector<std::string>& features, bool quiet)
+int FeatureUtility::DisableFeatures(const std::vector<std::string>& features)
{
String features_enabled_dir = GetFeaturesEnabledPath();
continue;
}
- if (!quiet)
- std::cout << "Disabling feature " << ConsoleColorTag(Console_ForegroundMagenta | Console_Bold) << feature
- << ConsoleColorTag(Console_Normal) << ". Make sure to restart Icinga 2 for these changes to take effect.\n";
+ std::cout << "Disabling feature " << ConsoleColorTag(Console_ForegroundMagenta | Console_Bold) << feature
+ << ConsoleColorTag(Console_Normal) << ". Make sure to restart Icinga 2 for these changes to take effect.\n";
}
if (!errors.empty()) {
static std::vector<String> GetFieldCompletionSuggestions(const String& word, bool enable);
- static int EnableFeatures(const std::vector<std::string>& features, bool quiet = false);
- static int DisableFeatures(const std::vector<std::string>& features, bool quiet = false);
+ static int EnableFeatures(const std::vector<std::string>& features);
+ static int DisableFeatures(const std::vector<std::string>& features);
static int ListFeatures(std::ostream& os = std::cout);
static bool GetFeatures(std::vector<String>& features, bool enable);
OpenLogFile();
}
- Dictionary::Ptr envData = ScriptGlobal::Get("EnvironmentInfo", &Empty);
- if (envData) {
- ScriptGlobal::Set("EnvironmentInfo", Empty);
- TcpSocket::Ptr listener = envData->Get("listener");
- if (!AddListener(listener)) {
- Log(LogCritical, "ApiListener")
- << "Failed to set up pre-configured agent listener.";
- Application::Exit(EXIT_FAILURE);
- }
- Utility::SaveJsonFile(envData->Get("meta_path"), 0600, envData->Get("config"));
- } else {
- /* create the primary JSON-RPC listener */
- if (!AddListener(GetBindHost(), GetBindPort())) {
- Log(LogCritical, "ApiListener")
- << "Cannot add listener on host '" << GetBindHost() << "' for port '" << GetBindPort() << "'.";
- Application::Exit(EXIT_FAILURE);
- }
+ /* create the primary JSON-RPC listener */
+ if (!AddListener(GetBindHost(), GetBindPort())) {
+ Log(LogCritical, "ApiListener")
+ << "Cannot add listener on host '" << GetBindHost() << "' for port '" << GetBindPort() << "'.";
+ Application::Exit(EXIT_FAILURE);
}
m_Timer = new Timer();
return master == GetLocalEndpoint();
}
-/**
- * Creates a new JSON-RPC listener using the specified TCP socket object.
- *
- * @param listener The TCP socket to use.
- */
-bool ApiListener::AddListener(const TcpSocket::Ptr& listener)
-{
- ObjectLock olock(this);
-
- std::shared_ptr<SSL_CTX> sslContext = m_SSLContext;
-
- if (!sslContext) {
- Log(LogCritical, "ApiListener", "SSL context is required for AddListener()");
- return false;
- }
-
- Log(LogInformation, "ApiListener")
- << "Adding pre-configured listener on address " << listener->GetClientAddress();
-
- std::thread thread(std::bind(&ApiListener::ListenerThreadProc, this, listener));
- thread.detach();
-
- m_Servers.insert(listener);
-
- return true;
-}
-
/**
* Creates a new JSON-RPC listener on the specified port.
*
void CleanupCertificateRequestsTimerHandler();
bool AddListener(const String& node, const String& service);
- bool AddListener(const TcpSocket::Ptr& listener);
void AddConnection(const Endpoint::Ptr& endpoint);
void NewClientHandler(const Socket::Ptr& client, const String& hostname, ConnectionRole role);