From aa6f497622da630af4e2309d7d9fd7e219206a3f Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Sat, 18 Oct 2014 21:06:28 +0200 Subject: [PATCH] CLI: Add agent command functionality (WIP) refs #7248 --- lib/cli/agentaddcommand.cpp | 7 +- lib/cli/agentblackandwhitelistcommand.cpp | 1 + lib/cli/agentlistcommand.cpp | 20 +++ lib/cli/agentlistcommand.hpp | 2 + lib/cli/agentremovecommand.cpp | 15 +- lib/cli/agentremovecommand.hpp | 1 + lib/cli/agentsetcommand.cpp | 3 +- lib/cli/agentsetupcommand.cpp | 1 + lib/cli/agentupdateconfigcommand.cpp | 1 + lib/cli/agentutility.cpp | 188 +++++++++++++++++++++- lib/cli/agentutility.hpp | 20 ++- lib/cli/agentwizardcommand.cpp | 1 + 12 files changed, 250 insertions(+), 10 deletions(-) diff --git a/lib/cli/agentaddcommand.cpp b/lib/cli/agentaddcommand.cpp index 2ccf48e0769..a252e17ca4b 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 + String name = ap[0]; + + if (!AgentUtility::AddAgent(name)) + return 1; return 0; } diff --git a/lib/cli/agentblackandwhitelistcommand.cpp b/lib/cli/agentblackandwhitelistcommand.cpp index 309911487a4..bb3a0ed385e 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; diff --git a/lib/cli/agentlistcommand.cpp b/lib/cli/agentlistcommand.cpp index 49abd13eb7b..670c319e132 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 1667a68041f..96295eaf797 100644 --- a/lib/cli/agentlistcommand.hpp +++ b/lib/cli/agentlistcommand.hpp @@ -37,6 +37,8 @@ class AgentListCommand : public CLICommand 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 f7565f0b267..d07eaa188ee 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); } /** @@ -54,6 +61,10 @@ int AgentRemoveCommand::Run(const boost::program_options::variables_map& vm, con } //ap[0] must contain name + String name = ap[0]; + + if (!AgentUtility::RemoveAgent(name)) + return 1; return 0; } diff --git a/lib/cli/agentremovecommand.hpp b/lib/cli/agentremovecommand.hpp index 2363ddf4ef8..cb716b311a0 100644 --- a/lib/cli/agentremovecommand.hpp +++ b/lib/cli/agentremovecommand.hpp @@ -37,6 +37,7 @@ class AgentRemoveCommand : public CLICommand 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 5644b78acdd..377520b0bde 100644 --- a/lib/cli/agentsetcommand.cpp +++ b/lib/cli/agentsetcommand.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -44,7 +45,7 @@ String AgentSetCommand::GetShortDescription(void) const void AgentSetCommand::InitParameters(boost::program_options::options_description& visibleDesc, boost::program_options::options_description& hiddenDesc) const { - /* Command doesn't support any parameters. */ + } /** diff --git a/lib/cli/agentsetupcommand.cpp b/lib/cli/agentsetupcommand.cpp index 154e3f0da67..f5d0b6c4f66 100644 --- a/lib/cli/agentsetupcommand.cpp +++ b/lib/cli/agentsetupcommand.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include diff --git a/lib/cli/agentupdateconfigcommand.cpp b/lib/cli/agentupdateconfigcommand.cpp index 472606e492e..ef23bf399eb 100644 --- a/lib/cli/agentupdateconfigcommand.cpp +++ b/lib/cli/agentupdateconfigcommand.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include diff --git a/lib/cli/agentutility.cpp b/lib/cli/agentutility.cpp index 09b90451cde..8b3e0d92169 100644 --- a/lib/cli/agentutility.cpp +++ b/lib/cli/agentutility.cpp @@ -18,25 +18,207 @@ ******************************************************************************/ #include "cli/agentutility.hpp" +#include "cli/clicommand.hpp" +#include "base/logger.hpp" +#include "base/application.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/"; +} + +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(GetRepositoryPath() + agent + ".repo"); + fp << "=======================\n"; + 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 << "=======================\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(GetRepositoryPath() + agent + ".repo"); + if (agent_obj) { + fp << JsonSerialize(agent_obj); + } + } } bool AgentUtility::AddAgent(const String& name) { - return true; + String path = GetRepositoryPath() + name + ".repo"; + + 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::AddAgentPeer(const String& name, const String& host, const String& port) +{ + String path = GetRepositoryPath() + name + ".peer"; + + 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) { + String path = GetRepositoryPath() + name + ".repo"; + + if (!RemoveAgentFile(path)) + return false; + + return true; +} + +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& attr, const Value& val) +bool AgentUtility::SetAgentAttribute(const String& name, const String& attr, const Value& val) { + GetAgentFromRepository(GetRepositoryPath() + name + ".repo"); + + return true; } + +bool AgentUtility::WriteAgentToRepository(const String& filename, const Dictionary::Ptr& item) +{ + Log(LogInformation, "cli", "Dumping config items 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 845e7754188..30cd4608c11 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,26 @@ namespace icinga class AgentUtility { public: - static void ListAgents(void); + static String GetRepositoryPath(void); + 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 AddAgentPeer(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 2ed2ef96fc5..57764eff02a 100644 --- a/lib/cli/agentwizardcommand.cpp +++ b/lib/cli/agentwizardcommand.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include