diff options
Diffstat (limited to 'lib/cli/nodeutility.cpp')
-rw-r--r-- | lib/cli/nodeutility.cpp | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/lib/cli/nodeutility.cpp b/lib/cli/nodeutility.cpp new file mode 100644 index 0000000..523532a --- /dev/null +++ b/lib/cli/nodeutility.cpp @@ -0,0 +1,378 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "cli/nodeutility.hpp" +#include "cli/clicommand.hpp" +#include "cli/variableutility.hpp" +#include "base/atomic-file.hpp" +#include "base/logger.hpp" +#include "base/application.hpp" +#include "base/tlsutility.hpp" +#include "base/convert.hpp" +#include "base/utility.hpp" +#include "base/scriptglobal.hpp" +#include "base/json.hpp" +#include "base/netstring.hpp" +#include "base/stdiostream.hpp" +#include "base/debug.hpp" +#include "base/objectlock.hpp" +#include "base/console.hpp" +#include "base/exception.hpp" +#include "base/configwriter.hpp" +#include <boost/algorithm/string/join.hpp> +#include <boost/algorithm/string/replace.hpp> +#include <fstream> +#include <iostream> + +using namespace icinga; + +String NodeUtility::GetConstantsConfPath() +{ + return Configuration::ConfigDir + "/constants.conf"; +} + +String NodeUtility::GetZonesConfPath() +{ + return Configuration::ConfigDir + "/zones.conf"; +} + +/* + * Node Setup helpers + */ + +int NodeUtility::GenerateNodeIcingaConfig(const String& endpointName, const String& zoneName, + const String& parentZoneName, const std::vector<std::string>& endpoints, + const std::vector<String>& globalZones) +{ + Array::Ptr config = new Array(); + + Array::Ptr myParentZoneMembers = new Array(); + + for (const String& endpoint : endpoints) { + /* extract all --endpoint arguments and store host,port info */ + std::vector<String> tokens = endpoint.Split(","); + + Dictionary::Ptr myParentEndpoint = new Dictionary(); + + if (tokens.size() > 1) { + String host = tokens[1].Trim(); + + if (!host.IsEmpty()) + myParentEndpoint->Set("host", host); + } + + if (tokens.size() > 2) { + String port = tokens[2].Trim(); + + if (!port.IsEmpty()) + myParentEndpoint->Set("port", port); + } + + String myEndpointName = tokens[0].Trim(); + myParentEndpoint->Set("__name", myEndpointName); + myParentEndpoint->Set("__type", "Endpoint"); + + /* save endpoint in master zone */ + myParentZoneMembers->Add(myEndpointName); + + config->Add(myParentEndpoint); + } + + /* add the parent zone to the config */ + config->Add(new Dictionary({ + { "__name", parentZoneName }, + { "__type", "Zone" }, + { "endpoints", myParentZoneMembers } + })); + + /* store the local generated node configuration */ + config->Add(new Dictionary({ + { "__name", endpointName }, + { "__type", "Endpoint" } + })); + + config->Add(new Dictionary({ + { "__name", zoneName }, + { "__type", "Zone" }, + { "parent", parentZoneName }, + { "endpoints", new Array({ endpointName }) } + })); + + for (const String& globalzone : globalZones) { + config->Add(new Dictionary({ + { "__name", globalzone }, + { "__type", "Zone" }, + { "global", true } + })); + } + + /* Write the newly generated configuration. */ + NodeUtility::WriteNodeConfigObjects(GetZonesConfPath(), config); + + return 0; +} + +int NodeUtility::GenerateNodeMasterIcingaConfig(const String& endpointName, const String& zoneName, + const std::vector<String>& globalZones) +{ + Array::Ptr config = new Array(); + + /* store the local generated node master configuration */ + config->Add(new Dictionary({ + { "__name", endpointName }, + { "__type", "Endpoint" } + })); + + config->Add(new Dictionary({ + { "__name", zoneName }, + { "__type", "Zone" }, + { "endpoints", new Array({ endpointName }) } + })); + + for (const String& globalzone : globalZones) { + config->Add(new Dictionary({ + { "__name", globalzone }, + { "__type", "Zone" }, + { "global", true } + })); + } + + /* Write the newly generated configuration. */ + NodeUtility::WriteNodeConfigObjects(GetZonesConfPath(), config); + + return 0; +} + +bool NodeUtility::WriteNodeConfigObjects(const String& filename, const Array::Ptr& objects) +{ + Log(LogInformation, "cli") + << "Dumping config items to file '" << filename << "'."; + + /* create a backup first */ + CreateBackupFile(filename); + + String path = Utility::DirName(filename); + + Utility::MkDirP(path, 0755); + + String user = Configuration::RunAsUser; + String group = Configuration::RunAsGroup; + + if (!Utility::SetFileOwnership(path, user, group)) { + Log(LogWarning, "cli") + << "Cannot set ownership for user '" << user << "' group '" << group << "' on path '" << path << "'. Verify it yourself!"; + } + + AtomicFile fp (filename, 0644); + + fp << "/*\n"; + fp << " * Generated by Icinga 2 node setup commands\n"; + fp << " * on " << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", Utility::GetTime()) << "\n"; + fp << " */\n\n"; + + ObjectLock olock(objects); + for (const Dictionary::Ptr& object : objects) { + SerializeObject(fp, object); + } + + fp << std::endl; + fp.Commit(); + + return true; +} + + +/* + * We generally don't overwrite files without backup before + */ +bool NodeUtility::CreateBackupFile(const String& target, bool isPrivate) +{ + if (!Utility::PathExists(target)) + return false; + + String backup = target + ".orig"; + + if (Utility::PathExists(backup)) { + Log(LogInformation, "cli") + << "Backup file '" << backup << "' already exists. Skipping backup."; + return false; + } + + Utility::CopyFile(target, backup); + +#ifndef _WIN32 + if (isPrivate) + chmod(backup.CStr(), 0600); +#endif /* _WIN32 */ + + Log(LogInformation, "cli") + << "Created backup file '" << backup << "'."; + + return true; +} + +void NodeUtility::SerializeObject(std::ostream& fp, const Dictionary::Ptr& object) +{ + fp << "object "; + ConfigWriter::EmitIdentifier(fp, object->Get("__type"), false); + fp << " "; + ConfigWriter::EmitValue(fp, 0, object->Get("__name")); + fp << " {\n"; + + ObjectLock olock(object); + for (const Dictionary::Pair& kv : object) { + if (kv.first == "__type" || kv.first == "__name") + continue; + + fp << "\t"; + ConfigWriter::EmitIdentifier(fp, kv.first, true); + fp << " = "; + ConfigWriter::EmitValue(fp, 1, kv.second); + fp << "\n"; + } + + fp << "}\n\n"; +} + +/* +* Returns true if the include is found, otherwise false +*/ +bool NodeUtility::GetConfigurationIncludeState(const String& value, bool recursive) { + String configurationFile = Configuration::ConfigDir + "/icinga2.conf"; + + Log(LogInformation, "cli") + << "Reading '" << configurationFile << "'."; + + std::ifstream ifp(configurationFile.CStr()); + + String affectedInclude = value; + + if (recursive) + affectedInclude = "include_recursive " + affectedInclude; + else + affectedInclude = "include " + affectedInclude; + + bool isIncluded = false; + + std::string line; + + while(std::getline(ifp, line)) { + /* + * Trying to find if the inclusion is enabled. + * First hit breaks out of the loop. + */ + + if (line.compare(0, affectedInclude.GetLength(), affectedInclude) == 0) { + isIncluded = true; + + /* + * We can safely break out here, since an enabled include always win. + */ + break; + } + } + + ifp.close(); + + return isIncluded; +} + +/* + * include = false, will comment out the include statement + * include = true, will add an include statement or uncomment a statement if one is existing + * resursive = false, will search for a non-resursive include statement + * recursive = true, will search for a resursive include statement + * Returns true on success, false if option was not found + */ +bool NodeUtility::UpdateConfiguration(const String& value, bool include, bool recursive) +{ + String configurationFile = Configuration::ConfigDir + "/icinga2.conf"; + + Log(LogInformation, "cli") + << "Updating '" << value << "' include in '" << configurationFile << "'."; + + NodeUtility::CreateBackupFile(configurationFile); + + std::ifstream ifp(configurationFile.CStr()); + AtomicFile ofp (configurationFile, 0644); + + String affectedInclude = value; + + if (recursive) + affectedInclude = "include_recursive " + affectedInclude; + else + affectedInclude = "include " + affectedInclude; + + bool found = false; + + std::string line; + + while (std::getline(ifp, line)) { + if (include) { + if (line.find("//" + affectedInclude) != std::string::npos || line.find("// " + affectedInclude) != std::string::npos) { + found = true; + ofp << "// Added by the node setup CLI command on " + << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", Utility::GetTime()) + << "\n" + affectedInclude + "\n"; + } else if (line.find(affectedInclude) != std::string::npos) { + found = true; + + Log(LogInformation, "cli") + << "Include statement '" + affectedInclude + "' already set."; + + ofp << line << "\n"; + } else { + ofp << line << "\n"; + } + } else { + if (line.find(affectedInclude) != std::string::npos) { + found = true; + ofp << "// Disabled by the node setup CLI command on " + << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", Utility::GetTime()) + << "\n// " + affectedInclude + "\n"; + } else { + ofp << line << "\n"; + } + } + } + + if (include && !found) { + ofp << "// Added by the node setup CLI command on " + << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", Utility::GetTime()) + << "\n" + affectedInclude + "\n"; + } + + ifp.close(); + ofp.Commit(); + + return (found || include); +} + +void NodeUtility::UpdateConstant(const String& name, const String& value) +{ + String constantsConfPath = NodeUtility::GetConstantsConfPath(); + + Log(LogInformation, "cli") + << "Updating '" << name << "' constant in '" << constantsConfPath << "'."; + + NodeUtility::CreateBackupFile(constantsConfPath); + + std::ifstream ifp(constantsConfPath.CStr()); + AtomicFile ofp (constantsConfPath, 0644); + + bool found = false; + + std::string line; + while (std::getline(ifp, line)) { + if (line.find("const " + name + " = ") != std::string::npos) { + ofp << "const " + name + " = \"" + value + "\"\n"; + found = true; + } else + ofp << line << "\n"; + } + + if (!found) + ofp << "const " + name + " = \"" + value + "\"\n"; + + ifp.close(); + ofp.Commit(); +} |