diff --git a/agent/icinga2-discover-agent.cmake b/agent/icinga2-discover-agent.cmake index b60f90827..cc2e9e72c 100644 --- a/agent/icinga2-discover-agent.cmake +++ b/agent/icinga2-discover-agent.cmake @@ -68,13 +68,13 @@ if cn == None: ssl_sock.close() -repository_file = "@CMAKE_INSTALL_FULL_LOCALSTATEDIR@/lib/icinga2/api/repository/" + hashlib.sha256(cn).hexdigest() +repository_file = "@CMAKE_INSTALL_FULL_LOCALSTATEDIR@/lib/icinga2/api/repository/" + hashlib.sha256(cn).hexdigest() + ".repo" fp = open(repository_file, "w") repository_info = { "endpoint": cn, "seen": time.time(), "zone": cn, "repository": {} } json.dump(repository_info, fp) fp.close() -peer_file = "@CMAKE_INSTALL_FULL_LOCALSTATEDIR@/lib/icinga2/agent/repository/" + hashlib.sha256(cn).hexdigest() + ".peer" +peer_file = "@CMAKE_INSTALL_FULL_LOCALSTATEDIR@/lib/icinga2/agent/repository/" + hashlib.sha256(cn).hexdigest() + ".settings" fp = open(peer_file, "w") peer_info = { "agent_host": host, "agent_port": port } json.dump(peer_info, fp) diff --git a/agent/icinga2-list-agents.cmake b/agent/icinga2-list-agents.cmake index 7cdbd0fbb..8e20389fa 100644 --- a/agent/icinga2-list-agents.cmake +++ b/agent/icinga2-list-agents.cmake @@ -28,7 +28,7 @@ for root, dirs, files in os.walk(repository_dir): if len(file) != 64: continue - fp = open(root + file, "r") + fp = open(root + file + ".repo", "r") repository_info = json.load(fp) fp.close() @@ -41,7 +41,7 @@ for root, dirs, files in os.walk(repository_dir): repository[repository_info["endpoint"]] = repository_info try: - fp = open(root + file + ".peer", "r") + fp = open(root + file + ".settings", "r") peer_info = json.load(fp) fp.close() diff --git a/lib/cli/agentaddcommand.cpp b/lib/cli/agentaddcommand.cpp index 2ccf48e07..98ad0c697 100644 --- a/lib/cli/agentaddcommand.cpp +++ b/lib/cli/agentaddcommand.cpp @@ -18,11 +18,13 @@ ******************************************************************************/ #include "cli/agentaddcommand.hpp" +#include "cli/agentutility.hpp" #include "base/logger.hpp" #include "base/application.hpp" #include #include #include +#include #include #include @@ -53,7 +55,10 @@ int AgentAddCommand::Run(const boost::program_options::variables_map& vm, const return 1; } - //ap[0] must contain name + if (!AgentUtility::AddAgent(ap[0])) { + Log(LogCritical, "cli", "Cannot add agent '" + ap[0] + "'."); + return 1; + } return 0; } diff --git a/lib/cli/agentblackandwhitelistcommand.cpp b/lib/cli/agentblackandwhitelistcommand.cpp index 309911487..5bf21f189 100644 --- a/lib/cli/agentblackandwhitelistcommand.cpp +++ b/lib/cli/agentblackandwhitelistcommand.cpp @@ -21,6 +21,7 @@ #include "base/logger.hpp" #include "base/application.hpp" #include +#include #include using namespace icinga; @@ -119,5 +120,6 @@ void BlackAndWhitelistCommand::InitParameters(boost::program_options::options_de */ int BlackAndWhitelistCommand::Run(const boost::program_options::variables_map& vm, const std::vector& ap) const { + Log(LogWarning, "cli", "TODO: Not implemented yet."); return 0; } diff --git a/lib/cli/agentlistcommand.cpp b/lib/cli/agentlistcommand.cpp index 49abd13eb..670c319e1 100644 --- a/lib/cli/agentlistcommand.cpp +++ b/lib/cli/agentlistcommand.cpp @@ -18,11 +18,14 @@ ******************************************************************************/ #include "cli/agentlistcommand.hpp" +#include "cli/agentutility.hpp" #include "base/logger.hpp" #include "base/application.hpp" +#include "base/console.hpp" #include #include #include +#include #include #include @@ -41,6 +44,13 @@ String AgentListCommand::GetShortDescription(void) const return "lists all agents"; } +void AgentListCommand::InitParameters(boost::program_options::options_description& visibleDesc, + boost::program_options::options_description& hiddenDesc) const +{ + visibleDesc.add_options() + ("batch", "list agents in json"); +} + /** * The entry point for the "agent list" CLI command. * @@ -53,5 +63,15 @@ int AgentListCommand::Run(const boost::program_options::variables_map& vm, const << "Ignoring parameters: " << boost::algorithm::join(ap, " "); } + if (vm.count("batch")) { + AgentUtility::PrintAgentsJson(std::cout); + std::cout << "\n"; + return 0; + } + + std::cout << "Configured agents: \n"; + AgentUtility::PrintAgents(std::cout); + std::cout << "\n"; + return 0; } diff --git a/lib/cli/agentlistcommand.hpp b/lib/cli/agentlistcommand.hpp index 1667a6804..96295eaf7 100644 --- a/lib/cli/agentlistcommand.hpp +++ b/lib/cli/agentlistcommand.hpp @@ -37,6 +37,8 @@ public: virtual String GetDescription(void) const; virtual String GetShortDescription(void) const; + virtual void InitParameters(boost::program_options::options_description& visibleDesc, + boost::program_options::options_description& hiddenDesc) const; virtual int Run(const boost::program_options::variables_map& vm, const std::vector& ap) const; }; diff --git a/lib/cli/agentremovecommand.cpp b/lib/cli/agentremovecommand.cpp index f7565f0b2..245589823 100644 --- a/lib/cli/agentremovecommand.cpp +++ b/lib/cli/agentremovecommand.cpp @@ -18,11 +18,13 @@ ******************************************************************************/ #include "cli/agentremovecommand.hpp" +#include "cli/agentutility.hpp" #include "base/logger.hpp" #include "base/application.hpp" #include #include #include +#include #include #include @@ -33,12 +35,17 @@ REGISTER_CLICOMMAND("agent/remove", AgentRemoveCommand); String AgentRemoveCommand::GetDescription(void) const { - return "Remove Icinga 2 agent."; + return "Removes Icinga 2 agent."; } String AgentRemoveCommand::GetShortDescription(void) const { - return "remove agent"; + return "removes agent"; +} + +std::vector AgentRemoveCommand::GetPositionalSuggestions(const String& word) const +{ + return AgentUtility::GetFieldCompletionSuggestions(word); } /** @@ -53,7 +60,10 @@ int AgentRemoveCommand::Run(const boost::program_options::variables_map& vm, con return 1; } - //ap[0] must contain name + if (!AgentUtility::RemoveAgent(ap[0])) { + Log(LogCritical, "cli", "Cannot remove agent '" + ap[0] + "'."); + return 1; + } return 0; } diff --git a/lib/cli/agentremovecommand.hpp b/lib/cli/agentremovecommand.hpp index 2363ddf4e..cb716b311 100644 --- a/lib/cli/agentremovecommand.hpp +++ b/lib/cli/agentremovecommand.hpp @@ -37,6 +37,7 @@ public: virtual String GetDescription(void) const; virtual String GetShortDescription(void) const; + virtual std::vector GetPositionalSuggestions(const String& word) const; virtual int Run(const boost::program_options::variables_map& vm, const std::vector& ap) const; }; diff --git a/lib/cli/agentsetcommand.cpp b/lib/cli/agentsetcommand.cpp index 5644b78ac..5fea91ef8 100644 --- a/lib/cli/agentsetcommand.cpp +++ b/lib/cli/agentsetcommand.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -33,18 +34,18 @@ REGISTER_CLICOMMAND("agent/set", AgentSetCommand); String AgentSetCommand::GetDescription(void) const { - return "Lists all Icinga 2 agents."; + return "Set agent attribute(s)."; } String AgentSetCommand::GetShortDescription(void) const { - return "lists all agents"; + return "set agent attributes"; } void AgentSetCommand::InitParameters(boost::program_options::options_description& visibleDesc, boost::program_options::options_description& hiddenDesc) const { - /* Command doesn't support any parameters. */ + } /** @@ -59,9 +60,7 @@ int AgentSetCommand::Run(const boost::program_options::variables_map& vm, const return 1; } - //ap[0] must contain name - //ap[1] must contain attr - //ap[2] must contain val + Log(LogWarning, "cli", "TODO: Not implemented yet."); return 0; } diff --git a/lib/cli/agentsetupcommand.cpp b/lib/cli/agentsetupcommand.cpp index 154e3f0da..6d9341d13 100644 --- a/lib/cli/agentsetupcommand.cpp +++ b/lib/cli/agentsetupcommand.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -45,9 +46,14 @@ void AgentSetupCommand::InitParameters(boost::program_options::options_descripti boost::program_options::options_description& hiddenDesc) const { visibleDesc.add_options() - ("master", po::value(), "The name of the Icinga 2 master") + ("zone", po::value(), "The name of the local zone") ("master_zone", po::value(), "The name of the master zone") - ("listen", po::value(), "Listen on host:port"); + ("endpoint", po::value >(), "Connect to remote endpoint(s) on host,port") + ("listen", po::value(), "Listen on host,port") + ("ticket", po::value(), "Generated ticket number for this request") + ("trustedcert", po::value(), "Trusted master certificate file") + ("cn", po::value(), "The certificate's common name") + ("master", po::value(), "Use setup for a master instance"); } /** @@ -62,5 +68,23 @@ int AgentSetupCommand::Run(const boost::program_options::variables_map& vm, cons << "Ignoring parameters: " << boost::algorithm::join(ap, " "); } + Log(LogWarning, "cli", "TODO: Not implemented yet."); + + if (vm.count("master")) { + SetupMaster(vm, ap); + } else { + SetupAgent(vm, ap); + } + return 0; } + +bool AgentSetupCommand::SetupMaster(const boost::program_options::variables_map& vm, const std::vector& ap) +{ + return true; +} + +bool AgentSetupCommand::SetupAgent(const boost::program_options::variables_map& vm, const std::vector& ap) +{ + return true; +} diff --git a/lib/cli/agentsetupcommand.hpp b/lib/cli/agentsetupcommand.hpp index 0e5b4f50c..d5b028fd5 100644 --- a/lib/cli/agentsetupcommand.hpp +++ b/lib/cli/agentsetupcommand.hpp @@ -40,6 +40,10 @@ public: virtual void InitParameters(boost::program_options::options_description& visibleDesc, boost::program_options::options_description& hiddenDesc) const; virtual int Run(const boost::program_options::variables_map& vm, const std::vector& ap) const; + +private: + static bool SetupMaster(const boost::program_options::variables_map& vm, const std::vector& ap); + static bool SetupAgent(const boost::program_options::variables_map& vm, const std::vector& ap); }; } diff --git a/lib/cli/agentupdateconfigcommand.cpp b/lib/cli/agentupdateconfigcommand.cpp index 472606e49..7f2e1732a 100644 --- a/lib/cli/agentupdateconfigcommand.cpp +++ b/lib/cli/agentupdateconfigcommand.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -53,5 +54,7 @@ int AgentUpdateConfigCommand::Run(const boost::program_options::variables_map& v << "Ignoring parameters: " << boost::algorithm::join(ap, " "); } + Log(LogWarning, "cli", "TODO: Not implemented yet."); + return 0; } diff --git a/lib/cli/agentutility.cpp b/lib/cli/agentutility.cpp index 09b90451c..a4aa07485 100644 --- a/lib/cli/agentutility.cpp +++ b/lib/cli/agentutility.cpp @@ -18,25 +18,237 @@ ******************************************************************************/ #include "cli/agentutility.hpp" +#include "cli/clicommand.hpp" +#include "base/logger.hpp" +#include "base/application.hpp" +#include "base/tlsutility.hpp" +#include "base/convert.hpp" +#include "base/serializer.hpp" +#include "base/netstring.hpp" +#include "base/stdiostream.hpp" +#include "base/debug.hpp" +#include "base/objectlock.hpp" +#include "base/console.hpp" +#include +#include +#include +#include using namespace icinga; -void AgentUtility::ListAgents(void) +String AgentUtility::GetRepositoryPath(void) { + return Application::GetLocalStateDir() + "/lib/icinga2/api/repository"; +} +String AgentUtility::GetAgentRepositoryFile(const String& name) +{ + return GetRepositoryPath() + "/" + SHA256(name) + ".repo"; +} + +String AgentUtility::GetAgentSettingsFile(const String& name) +{ + return GetRepositoryPath() + "/" + SHA256(name) + ".settings"; +} + + +std::vector AgentUtility::GetFieldCompletionSuggestions(const String& word) +{ + std::vector cache; + std::vector suggestions; + + GetAgents(cache); + + std::sort(cache.begin(), cache.end()); + + BOOST_FOREACH(const String& suggestion, cache) { + if (suggestion.Find(word) == 0) + suggestions.push_back(suggestion); + } + + return suggestions; +} + +void AgentUtility::PrintAgents(std::ostream& fp) +{ + std::vector agents; + GetAgents(agents); + + BOOST_FOREACH(const String& agent, agents) { + Dictionary::Ptr agent_obj = GetAgentFromRepository(GetAgentRepositoryFile(agent)); + fp << "Agent Name: " << agent << "\n"; + + if (agent_obj) { + fp << "Endpoint: " << agent_obj->Get("endpoint") << "\n"; + fp << "Zone: " << agent_obj->Get("zone") << "\n"; + fp << "Repository: "; + fp << std::setw(4); + PrintAgentRepository(fp, agent_obj->Get("repository")); + fp << std::setw(0) << "\n"; + } + } + + fp << "All agents: " << boost::algorithm::join(agents, " ") << "\n"; +} + +void AgentUtility::PrintAgentRepository(std::ostream& fp, const Dictionary::Ptr& repository) +{ + //TODO better formatting + fp << JsonSerialize(repository); +} + +void AgentUtility::PrintAgentsJson(std::ostream& fp) +{ + std::vector agents; + GetAgents(agents); + + BOOST_FOREACH(const String& agent, agents) { + Dictionary::Ptr agent_obj = GetAgentFromRepository(GetAgentRepositoryFile(agent)); + if (agent_obj) { + fp << JsonSerialize(agent_obj); + } + } } bool AgentUtility::AddAgent(const String& name) { - return true; + String path = GetAgentRepositoryFile(name); + + if (Utility::PathExists(path) ) { + Log(LogCritical, "cli") + << "Cannot add agent repo. '" + path + "' already exists.\n"; + return false; + } + + Dictionary::Ptr agent = make_shared(); + + agent->Set("seen", Utility::GetTime()); + agent->Set("endpoint", name); + agent->Set("zone", name); + agent->Set("repository", Empty); + + return WriteAgentToRepository(path, agent); +} + +bool AgentUtility::AddAgentSettings(const String& name, const String& host, const String& port) +{ + String path = GetAgentSettingsFile(name); + + Dictionary::Ptr peer = make_shared(); + + peer->Set("agent_host", host); + peer->Set("agent_port", port); + + return WriteAgentToRepository(path, peer); } bool AgentUtility::RemoveAgent(const String& name) { + if (!RemoveAgentFile(GetAgentRepositoryFile(name))) { + Log(LogCritical, "cli") + << "Cannot remove agent repo. '" + GetAgentRepositoryFile(name) + "' does not exist.\n"; + return false; + } + if (Utility::PathExists(GetAgentSettingsFile(name))) { + if (!RemoveAgentFile(GetAgentSettingsFile(name))) { + Log(LogWarning, "cli") + << "Cannot remove agent settings. '" + GetAgentSettingsFile(name) + "' does not exist.\n"; + return false; + } + } + return true; } -bool AgentUtility::SetAgentAttribute(const String& attr, const Value& val) +bool AgentUtility::RemoveAgentFile(const String& path) { + if (!Utility::PathExists(path)) { + Log(LogCritical, "cli", "Cannot remove '" + path + "'. Does not exist."); + return false; + } + + if (unlink(path.CStr()) < 0) { + Log(LogCritical, "cli", "Cannot remove file '" + path + + "'. Failed with error code " + Convert::ToString(errno) + ", \"" + Utility::FormatErrorNumber(errno) + "\"."); + return false; + } + return true; } + +bool AgentUtility::SetAgentAttribute(const String& name, const String& attr, const Value& val) +{ + String repo_path = GetAgentRepositoryFile(name); + Dictionary::Ptr repo = GetAgentFromRepository(repo_path); + + if (repo) { + repo->Set(attr, val); + WriteAgentToRepository(repo_path, repo); + return true; + } + + return false; +} + +bool AgentUtility::WriteAgentToRepository(const String& filename, const Dictionary::Ptr& item) +{ + Log(LogInformation, "cli", "Dumping agent to file '" + filename + "'"); + + String tempFilename = filename + ".tmp"; + + std::ofstream fp(tempFilename.CStr(), std::ofstream::out | std::ostream::trunc); + fp << JsonSerialize(item); + fp.close(); + +#ifdef _WIN32 + _unlink(filename.CStr()); +#endif /* _WIN32 */ + + if (rename(tempFilename.CStr(), filename.CStr()) < 0) { + BOOST_THROW_EXCEPTION(posix_error() + << boost::errinfo_api_function("rename") + << boost::errinfo_errno(errno) + << boost::errinfo_file_name(tempFilename)); + } + + return true; +} + +Dictionary::Ptr AgentUtility::GetAgentFromRepository(const String& filename) +{ + std::fstream fp; + fp.open(filename.CStr(), std::ifstream::in); + + if (!fp) + return Dictionary::Ptr(); + + String content((std::istreambuf_iterator(fp)), std::istreambuf_iterator()); + + std::cout << "Content: " << content << "\n"; + + fp.close(); + + return JsonDeserialize(content); +} + +bool AgentUtility::GetAgents(std::vector& agents) +{ + String path = GetRepositoryPath(); + + if (!Utility::Glob(path + "/*.repo", + boost::bind(&AgentUtility::CollectAgents, _1, boost::ref(agents)), GlobFile)) { + Log(LogCritical, "cli", "Cannot access path '" + path + "'."); + return false; + } + + return true; +} + +void AgentUtility::CollectAgents(const String& agent_file, std::vector& agents) +{ + String agent = Utility::BaseName(agent_file); + boost::algorithm::replace_all(agent, ".repo", ""); + + Log(LogDebug, "cli", "Adding agent: " + agent); + agents.push_back(agent); +} diff --git a/lib/cli/agentutility.hpp b/lib/cli/agentutility.hpp index 845e77541..488bf0a9a 100644 --- a/lib/cli/agentutility.hpp +++ b/lib/cli/agentutility.hpp @@ -21,7 +21,8 @@ #define AGENTUTILITY_H #include "base/i2-base.hpp" -#include "base/value.hpp" +#include "base/dictionary.hpp" +#include "base/string.hpp" namespace icinga { @@ -32,13 +33,28 @@ namespace icinga class AgentUtility { public: - static void ListAgents(void); + static String GetRepositoryPath(void); + static String GetAgentRepositoryFile(const String& name); + static String GetAgentSettingsFile(const String& name); + static std::vector GetFieldCompletionSuggestions(const String& word); + + static void PrintAgents(std::ostream& fp); + static void PrintAgentsJson(std::ostream& fp); + static void PrintAgentRepository(std::ostream& fp, const Dictionary::Ptr& repository); static bool AddAgent(const String& name); + static bool AddAgentSettings(const String& name, const String& host, const String& port); static bool RemoveAgent(const String& name); - static bool SetAgentAttribute(const String& attr, const Value& val); + static bool SetAgentAttribute(const String& name, const String& attr, const Value& val); + + static bool WriteAgentToRepository(const String& filename, const Dictionary::Ptr& item); + static Dictionary::Ptr GetAgentFromRepository(const String& filename); + + static bool GetAgents(std::vector& agents); private: AgentUtility(void); + static bool RemoveAgentFile(const String& path); + static void CollectAgents(const String& agent_file, std::vector& agents); }; } diff --git a/lib/cli/agentwizardcommand.cpp b/lib/cli/agentwizardcommand.cpp index 2ed2ef96f..8e39222e3 100644 --- a/lib/cli/agentwizardcommand.cpp +++ b/lib/cli/agentwizardcommand.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -53,5 +54,7 @@ int AgentWizardCommand::Run(const boost::program_options::variables_map& vm, con << "Ignoring parameters: " << boost::algorithm::join(ap, " "); } + Log(LogWarning, "cli", "TODO: Not implemented yet."); + return 0; } diff --git a/lib/icinga/apievents.cpp b/lib/icinga/apievents.cpp index 86c0458e0..4e8b3c6e4 100644 --- a/lib/icinga/apievents.cpp +++ b/lib/icinga/apievents.cpp @@ -1535,7 +1535,7 @@ Value ApiEvents::UpdateRepositoryAPIHandler(const MessageOrigin& origin, const D repository->Set(origin.FromZone->GetName(), hostInfo); } - String repositoryFile = GetRepositoryDir() + SHA256(params->Get("endpoint")); + String repositoryFile = GetRepositoryDir() + SHA256(params->Get("endpoint")) + ".repo"; String repositoryTempFile = repositoryFile + ".tmp"; std::ofstream fp(repositoryTempFile.CStr(), std::ofstream::out | std::ostream::trunc);