From 2972c6bfe8de08786dbf7de336d6111b6c91778f Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Mon, 20 Oct 2014 21:14:00 +0200 Subject: [PATCH] Implement the "variable" CLI command fixes #7370 --- icinga-app/icinga.cpp | 1 + lib/base/application.cpp | 21 +++++++ lib/base/application.hpp | 3 + lib/base/scriptvariable.cpp | 55 ++++++++++++++++ lib/base/scriptvariable.hpp | 16 +++-- lib/cli/CMakeLists.txt | 1 + lib/cli/daemoncommand.cpp | 7 ++- lib/cli/variablegetcommand.cpp | 108 ++++++++++++++++++++++++++++++++ lib/cli/variablegetcommand.hpp | 50 +++++++++++++++ lib/cli/variablelistcommand.cpp | 101 +++++++++++++++++++++++++++++ lib/cli/variablelistcommand.hpp | 51 +++++++++++++++ 11 files changed, 406 insertions(+), 8 deletions(-) create mode 100644 lib/cli/variablegetcommand.cpp create mode 100644 lib/cli/variablegetcommand.hpp create mode 100644 lib/cli/variablelistcommand.cpp create mode 100644 lib/cli/variablelistcommand.hpp diff --git a/icinga-app/icinga.cpp b/icinga-app/icinga.cpp index 4469fbbae..479c09f2b 100644 --- a/icinga-app/icinga.cpp +++ b/icinga-app/icinga.cpp @@ -228,6 +228,7 @@ int Main(void) Application::DeclareStatePath(Application::GetLocalStateDir() + "/lib/icinga2/icinga2.state"); Application::DeclareObjectsPath(Application::GetLocalStateDir() + "/cache/icinga2/icinga2.debug"); + Application::DeclareVarsPath(Application::GetLocalStateDir() + "/cache/icinga2/icinga2.vars"); Application::DeclarePidPath(Application::GetRunDir() + "/icinga2/icinga2.pid"); ConfigCompiler::AddIncludeSearchDir(Application::GetIncludeConfDir()); diff --git a/lib/base/application.cpp b/lib/base/application.cpp index 772a71e40..24c48d0b4 100644 --- a/lib/base/application.cpp +++ b/lib/base/application.cpp @@ -481,6 +481,7 @@ void Application::DisplayInfoMessage(bool skipVersion) << " Package data directory: " << GetPkgDataDir() << std::endl << " State path: " << GetStatePath() << std::endl << " Objects path: " << GetObjectsPath() << std::endl + << " Vars path: " << GetVarsPath() << std::endl << " PID path: " << GetPidPath() << std::endl << " Application type: " << GetApplicationType() << std::endl; } @@ -1012,6 +1013,26 @@ void Application::DeclareObjectsPath(const String& path) ScriptVariable::Set("ObjectsPath", path, false); } +/** +* Retrieves the path for the vars file. +* +* @returns The path. +*/ +String Application::GetVarsPath(void) +{ + return ScriptVariable::Get("VarsPath", &Empty); +} + +/** +* Sets the path for the vars file. +* +* @param path The new path. +*/ +void Application::DeclareVarsPath(const String& path) +{ + ScriptVariable::Set("VarsPath", path, false); +} + /** * Retrieves the path for the PID file. * diff --git a/lib/base/application.hpp b/lib/base/application.hpp index eb3713846..ac5becb0d 100644 --- a/lib/base/application.hpp +++ b/lib/base/application.hpp @@ -107,6 +107,9 @@ public: static String GetObjectsPath(void); static void DeclareObjectsPath(const String& path); + static String GetVarsPath(void); + static void DeclareVarsPath(const String& path); + static String GetPidPath(void); static void DeclarePidPath(const String& path); diff --git a/lib/base/scriptvariable.cpp b/lib/base/scriptvariable.cpp index 5174ad5eb..ef131fb50 100644 --- a/lib/base/scriptvariable.cpp +++ b/lib/base/scriptvariable.cpp @@ -19,6 +19,12 @@ #include "base/scriptvariable.hpp" #include "base/singleton.hpp" +#include "base/logger.hpp" +#include "base/stdiostream.hpp" +#include "base/netstring.hpp" +#include "base/convert.hpp" +#include +#include using namespace icinga; @@ -90,6 +96,55 @@ void ScriptVariable::Unregister(const String& name) ScriptVariableRegistry::GetInstance()->Unregister(name); } +void ScriptVariable::WriteVariablesFile(const String& filename) +{ + Log(LogInformation, "ScriptVariable") + << "Dumping variables to file '" << filename << "'"; + + String tempFilename = filename + ".tmp"; + + std::fstream fp; + fp.open(tempFilename.CStr(), std::ios_base::out); + + if (!fp) + BOOST_THROW_EXCEPTION(std::runtime_error("Could not open '" + tempFilename + "' file")); + + StdioStream::Ptr sfp = make_shared(&fp, false); + + BOOST_FOREACH(const ScriptVariableRegistry::ItemMap::value_type& kv, ScriptVariableRegistry::GetInstance()->GetItems()) { + Dictionary::Ptr persistentVariable = make_shared(); + + persistentVariable->Set("name", kv.first); + + ScriptVariable::Ptr sv = kv.second; + Value value = sv->GetData(); + + if (value.IsObject()) + value = Convert::ToString(value); + + persistentVariable->Set("value", value); + + String json = JsonSerialize(persistentVariable); + + NetString::WriteStringToStream(sfp, json); + } + + sfp->Close(); + + 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)); + } +} + ScriptVariableRegistry *ScriptVariableRegistry::GetInstance(void) { return Singleton::GetInstance(); diff --git a/lib/base/scriptvariable.hpp b/lib/base/scriptvariable.hpp index 491e8b05a..602c524c0 100644 --- a/lib/base/scriptvariable.hpp +++ b/lib/base/scriptvariable.hpp @@ -27,6 +27,14 @@ namespace icinga { +class ScriptVariable; + +class I2_BASE_API ScriptVariableRegistry : public Registry > +{ +public: + static ScriptVariableRegistry *GetInstance(void); +}; + /** * A script variables. * @@ -51,17 +59,13 @@ public: static Value Get(const String& name, const Value *defaultValue = NULL); static ScriptVariable::Ptr Set(const String& name, const Value& value, bool overwrite = true, bool make_const = false); + static void WriteVariablesFile(const String& filename); + private: Value m_Data; bool m_Constant; }; -class I2_BASE_API ScriptVariableRegistry : public Registry -{ -public: - static ScriptVariableRegistry *GetInstance(void); -}; - } #endif /* SCRIPTVARIABLE_H */ diff --git a/lib/cli/CMakeLists.txt b/lib/cli/CMakeLists.txt index 1e8a87573..104e109f0 100644 --- a/lib/cli/CMakeLists.txt +++ b/lib/cli/CMakeLists.txt @@ -24,6 +24,7 @@ set(cli_SOURCES objectlistcommand.cpp pkinewcacommand.cpp pkinewcertcommand.cpp pkisigncsrcommand.cpp pkirequestcommand.cpp pkiticketcommand.cpp repositoryobjectcommand.cpp + variablegetcommand.cpp variablelistcommand.cpp ) if(ICINGA2_UNITY_BUILD) diff --git a/lib/cli/daemoncommand.cpp b/lib/cli/daemoncommand.cpp index f5e6de297..0fe56ebc8 100644 --- a/lib/cli/daemoncommand.cpp +++ b/lib/cli/daemoncommand.cpp @@ -76,7 +76,8 @@ static void IncludeNonLocalZone(const String& zonePath) IncludeZoneDirRecursive(zonePath); } -static bool LoadConfigFiles(const boost::program_options::variables_map& vm, const String& appType, const String& objectsFile = String()) +static bool LoadConfigFiles(const boost::program_options::variables_map& vm, const String& appType, + const String& objectsFile = String(), const String& varsfile = String()) { ConfigCompilerContext::GetInstance()->Reset(); @@ -148,6 +149,8 @@ static bool LoadConfigFiles(const boost::program_options::variables_map& vm, con if (!result) return false; + ScriptVariable::WriteVariablesFile(varsfile); + return true; } @@ -327,7 +330,7 @@ int DaemonCommand::Run(const po::variables_map& vm, const std::vector +#include +#include +#include +#include + +using namespace icinga; +namespace po = boost::program_options; + +REGISTER_CLICOMMAND("variable/get", VariableGetCommand); + +String VariableGetCommand::GetDescription(void) const +{ + return "Prints the value of an Icinga 2 variable."; +} + +String VariableGetCommand::GetShortDescription(void) const +{ + return "gets a variable"; +} + +void VariableGetCommand::InitParameters(boost::program_options::options_description& visibleDesc, + boost::program_options::options_description& hiddenDesc) const +{ + visibleDesc.add_options() + ("current", "Uses the current value (i.e. from the running process, rather than from the vars file)"); +} + +/** + * The entry point for the "variable get" CLI command. + * + * @returns An exit status. + */ +int VariableGetCommand::Run(const boost::program_options::variables_map& vm, const std::vector& ap) const +{ + if (ap.size() != 1) { + Log(LogCritical, "cli", "Missing argument: variable name"); + return 1; + } + + if (vm.count("current")) { + std::cout << ScriptVariable::Get(ap[0], &Empty); + return 0; + } + + String varsfile = Application::GetVarsPath(); + + if (!Utility::PathExists(varsfile)) { + Log(LogCritical, "cli") + << "Cannot open variables file '" << varsfile << "'."; + Log(LogCritical, "cli", "Run 'icinga2 daemon -C' to validate config and generate the cache file."); + return 1; + } + + std::fstream fp; + fp.open(varsfile.CStr(), std::ios_base::in); + + StdioStream::Ptr sfp = make_shared(&fp, false); + unsigned long variables_count = 0; + + String message; + + while (NetString::ReadStringFromStream(sfp, &message)) { + Dictionary::Ptr variable = JsonDeserialize(message); + + if (variable->Get("name") == ap[0]) { + std::cout << variable->Get("value"); + break; + } + } + + sfp->Close(); + fp.close(); + + return 0; +} diff --git a/lib/cli/variablegetcommand.hpp b/lib/cli/variablegetcommand.hpp new file mode 100644 index 000000000..25c030461 --- /dev/null +++ b/lib/cli/variablegetcommand.hpp @@ -0,0 +1,50 @@ +/****************************************************************************** + * 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 VARIABLEGETCOMMAND_H +#define VARIABLEGETCOMMAND_H + +#include "base/dictionary.hpp" +#include "base/array.hpp" +#include "cli/clicommand.hpp" +#include + +namespace icinga +{ + +/** + * The "variable get" command. + * + * @ingroup cli + */ +class VariableGetCommand : public CLICommand +{ +public: + DECLARE_PTR_TYPEDEFS(VariableGetCommand); + + virtual String GetDescription(void) const; + virtual String GetShortDescription(void) const; + 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; +}; + +} + +#endif /* VARIABLEGETCOMMAND_H */ diff --git a/lib/cli/variablelistcommand.cpp b/lib/cli/variablelistcommand.cpp new file mode 100644 index 000000000..a5382ac48 --- /dev/null +++ b/lib/cli/variablelistcommand.cpp @@ -0,0 +1,101 @@ +/****************************************************************************** + * 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/variablelistcommand.hpp" +#include "base/logger.hpp" +#include "base/application.hpp" +#include "base/convert.hpp" +#include "base/dynamicobject.hpp" +#include "base/dynamictype.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 +#include + +using namespace icinga; +namespace po = boost::program_options; + +REGISTER_CLICOMMAND("variable/list", VariableListCommand); + +String VariableListCommand::GetDescription(void) const +{ + return "Lists all Icinga 2 variables."; +} + +String VariableListCommand::GetShortDescription(void) const +{ + return "lists all variables"; +} + +/** + * The entry point for the "variable list" CLI command. + * + * @returns An exit status. + */ +int VariableListCommand::Run(const boost::program_options::variables_map& vm, const std::vector& ap) const +{ + if (!ap.empty()) { + Log(LogWarning, "cli") + << "Ignoring parameters: " << boost::algorithm::join(ap, " "); + } + + String varsfile = Application::GetVarsPath(); + + if (!Utility::PathExists(varsfile)) { + Log(LogCritical, "cli") + << "Cannot open variables file '" << varsfile << "'."; + Log(LogCritical, "cli", "Run 'icinga2 daemon -C' to validate config and generate the cache file."); + return 1; + } + + std::fstream fp; + fp.open(varsfile.CStr(), std::ios_base::in); + + StdioStream::Ptr sfp = make_shared(&fp, false); + unsigned long variables_count = 0; + + String message; + + while (NetString::ReadStringFromStream(sfp, &message)) { + PrintVariable(std::cout, message); + variables_count++; + } + + sfp->Close(); + fp.close(); + + Log(LogNotice, "cli") + << "Parsed " << variables_count << " variables."; + + return 0; +} + +void VariableListCommand::PrintVariable(std::ostream& fp, const String& message) +{ + Dictionary::Ptr variable = JsonDeserialize(message); + + std::cout << variable->Get("name") << " = " << variable->Get("value") << "\n"; +} diff --git a/lib/cli/variablelistcommand.hpp b/lib/cli/variablelistcommand.hpp new file mode 100644 index 000000000..4ec2d3b18 --- /dev/null +++ b/lib/cli/variablelistcommand.hpp @@ -0,0 +1,51 @@ +/****************************************************************************** + * 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 VARIABLELISTCOMMAND_H +#define VARIABLELISTCOMMAND_H + +#include "base/dictionary.hpp" +#include "base/array.hpp" +#include "cli/clicommand.hpp" +#include + +namespace icinga +{ + +/** + * The "variable list" command. + * + * @ingroup cli + */ +class VariableListCommand : public CLICommand +{ +public: + DECLARE_PTR_TYPEDEFS(VariableListCommand); + + virtual String GetDescription(void) const; + virtual String GetShortDescription(void) const; + virtual int Run(const boost::program_options::variables_map& vm, const std::vector& ap) const; + +private: + static void PrintVariable(std::ostream& fp, const String& message); +}; + +} + +#endif /* VARIABLELISTCOMMAND_H */ -- 2.40.0