Remove bottom-up CLI commands

refs #4799
This commit is contained in:
Michael Friedrich 2017-09-20 11:18:59 +02:00
parent aa6ad55212
commit 601c10b997
22 changed files with 1 additions and 2584 deletions

View File

@ -18,15 +18,13 @@
set(cli_SOURCES
apisetupcommand.cpp apisetuputility.cpp
calistcommand.cpp casigncommand.cpp
nodeaddcommand.cpp nodeblackandwhitelistcommand.cpp nodelistcommand.cpp noderemovecommand.cpp
nodesetcommand.cpp nodesetupcommand.cpp nodeupdateconfigcommand.cpp nodewizardcommand.cpp nodeutility.cpp
nodesetupcommand.cpp nodewizardcommand.cpp nodeutility.cpp
clicommand.cpp
consolecommand.cpp
daemoncommand.cpp daemonutility.cpp
featureenablecommand.cpp featuredisablecommand.cpp featurelistcommand.cpp featureutility.cpp
objectlistcommand.cpp objectlistutility.cpp
pkinewcacommand.cpp pkinewcertcommand.cpp pkisigncsrcommand.cpp pkirequestcommand.cpp pkisavecertcommand.cpp pkiticketcommand.cpp
repositoryclearchangescommand.cpp repositorycommitcommand.cpp repositoryobjectcommand.cpp repositoryutility.cpp
variablegetcommand.cpp variablelistcommand.cpp variableutility.cpp
troubleshootcommand.cpp
)

View File

@ -1,65 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* 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/nodeaddcommand.hpp"
#include "cli/nodeutility.hpp"
#include "base/logger.hpp"
#include "base/application.hpp"
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <iostream>
#include <fstream>
#include <vector>
using namespace icinga;
namespace po = boost::program_options;
REGISTER_CLICOMMAND("node/add", NodeAddCommand);
String NodeAddCommand::GetDescription(void) const
{
return "Add Icinga 2 node.";
}
String NodeAddCommand::GetShortDescription(void) const
{
return "add node";
}
int NodeAddCommand::GetMinArguments(void) const
{
return 1;
}
bool NodeAddCommand::IsDeprecated(void) const
{
return true;
}
/**
* The entry point for the "node add" CLI command.
*
* @returns An exit status.
*/
int NodeAddCommand::Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const
{
NodeUtility::AddNode(ap[0]);
return 0;
}

View File

@ -1,47 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* 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 NODEADDCOMMAND_H
#define NODEADDCOMMAND_H
#include "cli/clicommand.hpp"
namespace icinga
{
/**
* The "node add" command.
*
* @ingroup cli
*/
class NodeAddCommand : public CLICommand
{
public:
DECLARE_PTR_TYPEDEFS(NodeAddCommand);
virtual String GetDescription(void) const override;
virtual String GetShortDescription(void) const override;
virtual bool IsDeprecated(void) const override;
virtual int GetMinArguments(void) const override;
virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const override;
};
}
#endif /* NODEADDCOMMAND_H */

View File

@ -1,153 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* 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/nodeblackandwhitelistcommand.hpp"
#include "cli/nodeutility.hpp"
#include "base/logger.hpp"
#include "base/application.hpp"
#include "base/objectlock.hpp"
#include "base/json.hpp"
#include <iostream>
#include <fstream>
using namespace icinga;
namespace po = boost::program_options;
REGISTER_BLACKANDWHITELIST_CLICOMMAND(whitelist);
REGISTER_BLACKANDWHITELIST_CLICOMMAND(blacklist);
BlackAndWhitelistCommand::BlackAndWhitelistCommand(const String& type, BlackAndWhitelistCommandType command)
: m_Type(type), m_Command(command)
{ }
String BlackAndWhitelistCommand::GetDescription(void) const
{
String description;
switch (m_Command) {
case BlackAndWhitelistCommandAdd:
description = "Adds a new";
break;
case BlackAndWhitelistCommandRemove:
description = "Removes a";
break;
case BlackAndWhitelistCommandList:
description = "Lists all";
break;
}
description += " " + m_Type + " filter";
if (m_Command == BlackAndWhitelistCommandList)
description += "s";
return description;
}
String BlackAndWhitelistCommand::GetShortDescription(void) const
{
String description;
switch (m_Command) {
case BlackAndWhitelistCommandAdd:
description = "adds a new";
break;
case BlackAndWhitelistCommandRemove:
description = "removes a";
break;
case BlackAndWhitelistCommandList:
description = "lists all";
break;
}
description += " " + m_Type + " filter";
if (m_Command == BlackAndWhitelistCommandList)
description += "s";
return description;
}
bool BlackAndWhitelistCommand::IsDeprecated(void) const
{
return true;
}
void BlackAndWhitelistCommand::InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const
{
if (m_Command == BlackAndWhitelistCommandAdd || m_Command == BlackAndWhitelistCommandRemove) {
visibleDesc.add_options()
("zone", po::value<std::string>(), "The name of the zone")
("host", po::value<std::string>(), "The name of the host")
("service", po::value<std::string>(), "The name of the service");
}
}
/**
* The entry point for the "node <whitelist/blacklist> <add/remove/list>" CLI command.
*
* @returns An exit status.
*/
int BlackAndWhitelistCommand::Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const
{
if (m_Command == BlackAndWhitelistCommandAdd) {
if (!vm.count("zone")) {
Log(LogCritical, "cli", "At least the zone name filter is required!");
return 1;
}
if (!vm.count("host")) {
Log(LogCritical, "cli", "At least the host name filter is required!");
return 1;
}
String service_filter;
if (vm.count("service"))
service_filter = vm["service"].as<std::string>();
return NodeUtility::UpdateBlackAndWhiteList(m_Type, vm["zone"].as<std::string>(), vm["host"].as<std::string>(), service_filter);
} else if (m_Command == BlackAndWhitelistCommandList) {
return NodeUtility::PrintBlackAndWhiteList(std::cout, m_Type);
} else if (m_Command == BlackAndWhitelistCommandRemove) {
if (!vm.count("zone")) {
Log(LogCritical, "cli", "The zone name filter is required!");
return 1;
}
if (!vm.count("host")) {
Log(LogCritical, "cli", "The host name filter is required!");
return 1;
}
String zone_filter = vm["zone"].as<std::string>();
String host_filter = vm["host"].as<std::string>();
String service_filter;
if (vm.count("service")) {
service_filter = vm["service"].as<std::string>();
}
return NodeUtility::RemoveBlackAndWhiteList(m_Type, vm["zone"].as<std::string>(), vm["host"].as<std::string>(), service_filter);
}
return 0;
}

View File

@ -1,80 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* 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 BLACKANDWHITELISTCOMMAND_H
#define BLACKANDWHITELISTCOMMAND_H
#include "cli/clicommand.hpp"
#include <boost/algorithm/string/case_conv.hpp>
namespace icinga
{
enum BlackAndWhitelistCommandType
{
BlackAndWhitelistCommandAdd,
BlackAndWhitelistCommandRemove,
BlackAndWhitelistCommandList
};
/**
* The "repository <type> <add/remove/list>" command.
*
* @ingroup cli
*/
class I2_CLI_API BlackAndWhitelistCommand : public CLICommand
{
public:
DECLARE_PTR_TYPEDEFS(BlackAndWhitelistCommand);
BlackAndWhitelistCommand(const String& type, BlackAndWhitelistCommandType command);
virtual String GetDescription(void) const override;
virtual String GetShortDescription(void) const override;
virtual bool IsDeprecated(void) const override;
virtual void InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const override;
virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const override;
private:
String m_Type;
BlackAndWhitelistCommandType m_Command;
};
#define REGISTER_BLACKANDWHITELIST_CLICOMMAND(type) \
INITIALIZE_ONCE([]() { \
String ltype = #type; \
boost::algorithm::to_lower(ltype); \
\
std::vector<String> name; \
name.push_back("node"); \
name.push_back(ltype); \
name.push_back("add"); \
CLICommand::Register(name, new BlackAndWhitelistCommand(#type, BlackAndWhitelistCommandAdd)); \
\
name[2] = "remove"; \
CLICommand::Register(name, new BlackAndWhitelistCommand(#type, BlackAndWhitelistCommandRemove)); \
\
name[2] = "list"; \
CLICommand::Register(name, new BlackAndWhitelistCommand(#type, BlackAndWhitelistCommandList)); \
})
}
#endif /* BLACKANDWHITELISTCOMMAND_H */

View File

@ -1,76 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* 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/nodelistcommand.hpp"
#include "cli/nodeutility.hpp"
#include "base/logger.hpp"
#include "base/application.hpp"
#include "base/console.hpp"
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <iostream>
#include <fstream>
#include <vector>
using namespace icinga;
namespace po = boost::program_options;
REGISTER_CLICOMMAND("node/list", NodeListCommand);
String NodeListCommand::GetDescription(void) const
{
return "Lists all Icinga 2 nodes.";
}
String NodeListCommand::GetShortDescription(void) const
{
return "lists all nodes";
}
bool NodeListCommand::IsDeprecated(void) const
{
return true;
}
void NodeListCommand::InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const
{
visibleDesc.add_options()
("batch", "list nodes in json");
}
/**
* The entry point for the "node list" CLI command.
*
* @returns An exit status.
*/
int NodeListCommand::Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const
{
if (!ap.empty()) {
Log(LogWarning, "cli")
<< "Ignoring parameters: " << boost::algorithm::join(ap, " ");
}
if (vm.count("batch"))
NodeUtility::PrintNodesJson(std::cout);
else
NodeUtility::PrintNodes(std::cout);
return 0;
}

View File

@ -1,48 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* 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 NODELISTCOMMAND_H
#define NODELISTCOMMAND_H
#include "cli/clicommand.hpp"
namespace icinga
{
/**
* The "node list" command.
*
* @ingroup cli
*/
class NodeListCommand : public CLICommand
{
public:
DECLARE_PTR_TYPEDEFS(NodeListCommand);
virtual String GetDescription(void) const override;
virtual String GetShortDescription(void) const override;
virtual bool IsDeprecated(void) const override;
virtual void InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const override;
virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const override;
};
}
#endif /* NODELISTCOMMAND_H */

View File

@ -1,77 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* 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/noderemovecommand.hpp"
#include "cli/nodeutility.hpp"
#include "base/logger.hpp"
#include "base/application.hpp"
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <iostream>
#include <fstream>
#include <vector>
using namespace icinga;
namespace po = boost::program_options;
REGISTER_CLICOMMAND("node/remove", NodeRemoveCommand);
String NodeRemoveCommand::GetDescription(void) const
{
return "Removes Icinga 2 node.";
}
String NodeRemoveCommand::GetShortDescription(void) const
{
return "removes node";
}
std::vector<String> NodeRemoveCommand::GetPositionalSuggestions(const String& word) const
{
return NodeUtility::GetNodeCompletionSuggestions(word);
}
int NodeRemoveCommand::GetMinArguments(void) const
{
return 1;
}
int NodeRemoveCommand::GetMaxArguments(void) const
{
return -1;
}
bool NodeRemoveCommand::IsDeprecated(void) const
{
return true;
}
/**
* The entry point for the "node remove" CLI command.
*
* @returns An exit status.
*/
int NodeRemoveCommand::Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const
{
for (const String& node : ap) {
NodeUtility::RemoveNode(node);
}
return 0;
}

View File

@ -1,49 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* 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 NODEREMOVECOMMAND_H
#define NODEREMOVECOMMAND_H
#include "cli/clicommand.hpp"
namespace icinga
{
/**
* The "node remove" command.
*
* @ingroup cli
*/
class NodeRemoveCommand : public CLICommand
{
public:
DECLARE_PTR_TYPEDEFS(NodeRemoveCommand);
virtual String GetDescription(void) const override;
virtual String GetShortDescription(void) const override;
virtual bool IsDeprecated(void) const override;
virtual int GetMinArguments(void) const override;
virtual int GetMaxArguments(void) const override;
virtual std::vector<String> GetPositionalSuggestions(const String& word) const override;
virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const override;
};
}
#endif /* NODEREMOVECOMMAND_H */

View File

@ -1,94 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* 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/nodesetcommand.hpp"
#include "cli/nodeutility.hpp"
#include "base/logger.hpp"
#include "base/application.hpp"
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <iostream>
#include <fstream>
#include <vector>
using namespace icinga;
namespace po = boost::program_options;
REGISTER_CLICOMMAND("node/set", NodeSetCommand);
String NodeSetCommand::GetDescription(void) const
{
return "Set node attribute(s).";
}
String NodeSetCommand::GetShortDescription(void) const
{
return "set node attributes";
}
void NodeSetCommand::InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const
{
visibleDesc.add_options()
("host", po::value<std::string>(), "Icinga 2 host")
("port", po::value<std::string>(), "Icinga 2 port")
("log_duration", po::value<double>(), "Log duration (in seconds)");
}
int NodeSetCommand::GetMinArguments(void) const
{
return 1;
}
bool NodeSetCommand::IsDeprecated(void) const
{
return true;
}
/**
* The entry point for the "node set" CLI command.
*
* @returns An exit status.
*/
int NodeSetCommand::Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const
{
String repoFile = NodeUtility::GetNodeRepositoryFile(ap[0]);
if (!Utility::PathExists(repoFile)) {
Log(LogCritical, "cli")
<< "Node '" << ap[0] << "' does not exist.";
return 1;
}
String host, port = "5665";
double log_duration = 24 * 60 * 60;
if (vm.count("host"))
host = vm["host"].as<std::string>();
if (vm.count("port"))
port = vm["port"].as<std::string>();
if (vm.count("log_duration"))
log_duration = vm["log_duration"].as<double>();
NodeUtility::AddNodeSettings(ap[0], host, port, log_duration);
return 0;
}

View File

@ -1,49 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* 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 NODESETCOMMAND_H
#define NODESETCOMMAND_H
#include "cli/clicommand.hpp"
namespace icinga
{
/**
* The "node set" command.
*
* @ingroup cli
*/
class NodeSetCommand : public CLICommand
{
public:
DECLARE_PTR_TYPEDEFS(NodeSetCommand);
virtual String GetDescription(void) const override;
virtual String GetShortDescription(void) const override;
virtual int GetMinArguments(void) const override;
virtual bool IsDeprecated(void) const override;
virtual void InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const override;
virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const override;
};
}
#endif /* NODESETCOMMAND_H */

View File

@ -1,427 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* 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/nodeupdateconfigcommand.hpp"
#include "cli/nodeutility.hpp"
#include "cli/repositoryutility.hpp"
#include "cli/variableutility.hpp"
#include "base/logger.hpp"
#include "base/console.hpp"
#include "base/application.hpp"
#include "base/tlsutility.hpp"
#include "base/json.hpp"
#include "base/objectlock.hpp"
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <iostream>
#include <fstream>
#include <vector>
using namespace icinga;
namespace po = boost::program_options;
REGISTER_CLICOMMAND("node/update-config", NodeUpdateConfigCommand);
String NodeUpdateConfigCommand::GetDescription(void) const
{
return "Update Icinga 2 node config.";
}
String NodeUpdateConfigCommand::GetShortDescription(void) const
{
return "update node config";
}
ImpersonationLevel NodeUpdateConfigCommand::GetImpersonationLevel(void) const
{
return ImpersonateRoot;
}
bool NodeUpdateConfigCommand::IsDeprecated(void) const
{
return true;
}
/**
* The entry point for the "node update-config" CLI command.
*
* @returns An exit status.
*/
int NodeUpdateConfigCommand::Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const
{
//If there are changes pending, abort the current operation
if (RepositoryUtility::ChangeLogHasPendingChanges()) {
Log(LogWarning, "cli")
<< "There are pending changes for commit.\n"
<< "Please review and commit them using 'icinga2 repository commit [--simulate]'\n"
<< "or drop them using 'icinga2 repository clear-changes' before proceeding.";
return 1;
}
String inventory_path = NodeUtility::GetRepositoryPath() + "/inventory.index";
Dictionary::Ptr old_inventory = new Dictionary();
if (Utility::PathExists(inventory_path)) {
old_inventory = Utility::LoadJsonFile(inventory_path);
}
Dictionary::Ptr inventory = new Dictionary();
Log(LogInformation, "cli")
<< "Updating node configuration for ";
NodeUtility::PrintNodes(std::cout);
/* cache all existing object configs only once and pass it to AddObject() */
std::vector<String> object_paths = RepositoryUtility::GetObjects();
/* cache all existing changes only once and pass it to AddObject() */
Array::Ptr changes = new Array();
RepositoryUtility::GetChangeLog(boost::bind(RepositoryUtility::CollectChange, _1, changes));
std::vector<Dictionary::Ptr> nodes = NodeUtility::GetNodes();
/* first make sure that all nodes are valid and should not be removed */
for (const Dictionary::Ptr& node : nodes) {
Dictionary::Ptr repository = node->Get("repository");
String zone = node->Get("zone");
String endpoint = node->Get("endpoint");
String node_name = endpoint;
/* store existing structure in index */
inventory->Set(endpoint, node);
}
if (old_inventory) {
/* check if there are objects inside the old_inventory which do not exist anymore */
ObjectLock ulock(old_inventory);
for (const Dictionary::Pair& old_node_objs : old_inventory) {
String old_node_name = old_node_objs.first;
/* check if the node was dropped */
if (!inventory->Contains(old_node_name)) {
Log(LogInformation, "cli")
<< "Node update found old node '" << old_node_name << "'. Removing it and all of its hosts/services.";
//TODO Remove an node and all of his hosts
Dictionary::Ptr old_node = old_node_objs.second;
Dictionary::Ptr old_node_repository = old_node->Get("repository");
if (old_node_repository) {
ObjectLock olock(old_node_repository);
for (const Dictionary::Pair& kv : old_node_repository) {
String host = kv.first;
Dictionary::Ptr host_attrs = new Dictionary();
host_attrs->Set("name", host);
RepositoryUtility::RemoveObject(host, "Host", host_attrs, changes); //this removes all services for this host as well
}
}
String zone = old_node->Get("zone");
String endpoint = old_node->Get("endpoint");
Dictionary::Ptr zone_attrs = new Dictionary();
zone_attrs->Set("name", zone);
RepositoryUtility::RemoveObject(zone, "Zone", zone_attrs, changes);
Dictionary::Ptr endpoint_attrs = new Dictionary();
endpoint_attrs->Set("name", endpoint);
RepositoryUtility::RemoveObject(endpoint, "Endpoint", endpoint_attrs, changes);
} else {
/* get the current node */
Dictionary::Ptr new_node = inventory->Get(old_node_name);
Dictionary::Ptr new_node_repository = new_node->Get("repository");
Dictionary::Ptr old_node = old_node_objs.second;
Dictionary::Ptr old_node_repository = old_node->Get("repository");
if (old_node_repository) {
ObjectLock xlock(old_node_repository);
for (const Dictionary::Pair& kv : old_node_repository) {
String old_host = kv.first;
if (old_host == "localhost") {
Log(LogWarning, "cli")
<< "Ignoring host '" << old_host << "'. Please make sure to configure a unique name on your node '" << old_node_name << "'.";
continue;
}
/* check against black/whitelist before trying to remove host */
if (NodeUtility::CheckAgainstBlackAndWhiteList("blacklist", old_node_name, old_host, Empty) &&
!NodeUtility::CheckAgainstBlackAndWhiteList("whitelist", old_node_name, old_host, Empty)) {
Log(LogWarning, "cli")
<< "Host '" << old_node_name << "' on node '" << old_node_name << "' is blacklisted, but not whitelisted. Skipping.";
continue;
}
if (!new_node_repository->Contains(old_host)) {
Log(LogInformation, "cli")
<< "Node update found old host '" << old_host << "' on node '" << old_node_name << "'. Removing it.";
Dictionary::Ptr host_attrs = new Dictionary();
host_attrs->Set("name", old_host);
RepositoryUtility::RemoveObject(old_host, "Host", host_attrs, changes); //this will remove all services for this host too
} else {
/* host exists, now check all services for this host */
Array::Ptr old_services = kv.second;
Array::Ptr new_services = new_node_repository->Get(old_host);
ObjectLock ylock(old_services);
for (const String& old_service : old_services) {
/* check against black/whitelist before trying to remove service */
if (NodeUtility::CheckAgainstBlackAndWhiteList("blacklist", old_node_name, old_host, old_service) &&
!NodeUtility::CheckAgainstBlackAndWhiteList("whitelist", old_node_name, old_host, old_service)) {
Log(LogWarning, "cli")
<< "Service '" << old_service << "' on host '" << old_host << "' on node '"
<< old_node_name << "' is blacklisted, but not whitelisted. Skipping.";
continue;
}
if (!new_services->Contains(old_service)) {
Log(LogInformation, "cli")
<< "Node update found old service '" << old_service << "' on host '" << old_host
<< "' on node '" << old_node_name << "'. Removing it.";
Dictionary::Ptr service_attrs = new Dictionary();
service_attrs->Set("name", old_service);
service_attrs->Set("host_name", old_host);
RepositoryUtility::RemoveObject(old_service, "Service", service_attrs, changes);
}
}
}
}
}
}
}
}
/* next iterate over all nodes and add hosts/services */
for (const Dictionary::Ptr& node : nodes) {
Dictionary::Ptr repository = node->Get("repository");
String zone = node->Get("zone");
String endpoint = node->Get("endpoint");
String node_name = endpoint;
Dictionary::Ptr host_services = new Dictionary();
if (NodeUtility::CheckAgainstBlackAndWhiteList("blacklist", node_name, "*", Empty) &&
!NodeUtility::CheckAgainstBlackAndWhiteList("whitelist", node_name, "*", Empty)) {
Log(LogWarning, "cli")
<< "Skipping node '" << node_name << "' on blacklist.";
continue;
}
Log(LogInformation, "cli")
<< "Adding host '" << zone << "' to the repository.";
Dictionary::Ptr host_attrs = new Dictionary();
host_attrs->Set("__name", zone);
host_attrs->Set("name", zone);
host_attrs->Set("check_command", "cluster-zone");
Array::Ptr host_imports = new Array();
host_imports->Add("satellite-host"); //default host node template
host_attrs->Set("import", host_imports);
if (!RepositoryUtility::AddObject(object_paths, zone, "Host", host_attrs, changes, false)) {
Log(LogWarning, "cli")
<< "Cannot add node host '" << zone << "' to the config repository!\n";
}
if (repository) {
ObjectLock olock(repository);
for (const Dictionary::Pair& kv : repository) {
String host = kv.first;
String host_pattern = host + ".conf";
bool skip_host = false;
if (host == "localhost") {
Log(LogWarning, "cli")
<< "Ignoring host '" << host << "'. Please make sure to configure a unique name on your node '" << endpoint << "'.";
continue;
}
for (const String& object_path : object_paths) {
if (object_path.Contains(host_pattern)) {
Log(LogNotice, "cli")
<< "Host '" << host << "' already exists.";
skip_host = true;
break;
}
}
/* host has already been created above */
if (host == zone)
skip_host = true;
bool host_was_blacklisted = false;
/* check against black/whitelist before trying to add host */
if (NodeUtility::CheckAgainstBlackAndWhiteList("blacklist", node_name, host, Empty) &&
!NodeUtility::CheckAgainstBlackAndWhiteList("whitelist", node_name, host, Empty)) {
Log(LogWarning, "cli")
<< "Host '" << host << "' on node '" << node_name << "' is blacklisted, but not whitelisted. Not creating host object.";
skip_host = true;
host_was_blacklisted = true; //check this for services on this blacklisted host
}
if (!skip_host) {
/* add a new host to the config repository */
Dictionary::Ptr host_attrs = new Dictionary();
host_attrs->Set("__name", host);
host_attrs->Set("name", host);
if (host == zone)
host_attrs->Set("check_command", "cluster-zone");
else {
host_attrs->Set("check_command", "dummy");
host_attrs->Set("zone", zone);
}
Array::Ptr host_imports = new Array();
host_imports->Add("satellite-host"); //default host node template
host_attrs->Set("import", host_imports);
RepositoryUtility::AddObject(object_paths, host, "Host", host_attrs, changes, false);
}
/* special condition: what if the host was blacklisted before, but the services should be generated? */
if (host_was_blacklisted) {
Log(LogNotice, "cli")
<< "Host '" << host << "' was blacklisted. Won't generate any services.";
continue;
}
Array::Ptr services = kv.second;
if (services->GetLength() == 0) {
Log(LogNotice, "cli")
<< "Host '" << host << "' without services.";
continue;
}
ObjectLock xlock(services);
for (const String& service : services) {
bool skip_service = false;
String service_pattern = host + "/" + service + ".conf";
for (const String& object_path : object_paths) {
if (object_path.Contains(service_pattern)) {
Log(LogNotice, "cli")
<< "Service '" << service << "' on Host '" << host << "' already exists.";
skip_service = true;
break;
}
}
/* check against black/whitelist before trying to add service */
if (NodeUtility::CheckAgainstBlackAndWhiteList("blacklist", endpoint, host, service) &&
!NodeUtility::CheckAgainstBlackAndWhiteList("whitelist", endpoint, host, service)) {
Log(LogWarning, "cli")
<< "Service '" << service << "' on host '" << host << "' on node '"
<< node_name << "' is blacklisted, but not whitelisted. Not creating service object.";
skip_service = true;
}
if (skip_service)
continue;
/* add a new service for this host to the config repository */
Dictionary::Ptr service_attrs = new Dictionary();
String long_name = host + "!" + service; //use NameComposer?
service_attrs->Set("__name", long_name);
service_attrs->Set("name", service);
service_attrs->Set("host_name", host); //Required for host-service relation
service_attrs->Set("check_command", "dummy");
service_attrs->Set("zone", zone);
Array::Ptr service_imports = new Array();
service_imports->Add("satellite-service"); //default service node template
service_attrs->Set("import", service_imports);
if (!RepositoryUtility::AddObject(object_paths, service, "Service", service_attrs, changes, false))
continue;
}
}
}
/* write a new zone and endpoint for the node */
Dictionary::Ptr endpoint_attrs = new Dictionary();
endpoint_attrs->Set("__name", endpoint);
endpoint_attrs->Set("name", endpoint);
Dictionary::Ptr settings = node->Get("settings");
if (settings) {
if (settings->Contains("host"))
endpoint_attrs->Set("host", settings->Get("host"));
if (settings->Contains("port"))
endpoint_attrs->Set("port", settings->Get("port"));
}
Log(LogInformation, "cli")
<< "Adding endpoint '" << endpoint << "' to the repository.";
if (!RepositoryUtility::AddObject(object_paths, endpoint, "Endpoint", endpoint_attrs, changes, false)) {
Log(LogWarning, "cli")
<< "Cannot add node endpoint '" << endpoint << "' to the config repository!\n";
}
Dictionary::Ptr zone_attrs = new Dictionary();
Array::Ptr zone_members = new Array();
zone_members->Add(endpoint);
zone_attrs->Set("__name", zone);
zone_attrs->Set("name", zone);
zone_attrs->Set("endpoints", zone_members);
String parent_zone = VariableUtility::GetVariable("ZoneName");
if (parent_zone.IsEmpty()) {
Log(LogWarning, "cli")
<< "Variable 'ZoneName' is not set. Falling back to using 'master' as default. Please verify the generated configuration.";
parent_zone = "master";
}
zone_attrs->Set("parent", parent_zone);
Log(LogInformation, "cli")
<< "Adding zone '" << zone << "' to the repository.";
if (!RepositoryUtility::AddObject(object_paths, zone, "Zone", zone_attrs, changes, false)) {
Log(LogWarning, "cli")
<< "Cannot add node zone '" << zone << "' to the config repository!\n";
}
}
Log(LogInformation, "cli", "Committing node configuration.");
RepositoryUtility::PrintChangeLog(std::cout);
std::cout << "\n";
RepositoryUtility::CommitChangeLog();
/* store the new inventory for next run */
NodeUtility::CreateRepositoryPath();
Utility::SaveJsonFile(inventory_path, 0600, inventory);
std::cout << "Make sure to reload Icinga 2 for these changes to take effect." << std::endl;
return 0;
}

View File

@ -1,47 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* 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 NODEUPDATECONFIGCOMMAND_H
#define NODEUPDATECONFIGCOMMAND_H
#include "cli/clicommand.hpp"
namespace icinga
{
/**
* The "agent update-config" command.
*
* @ingroup cli
*/
class NodeUpdateConfigCommand : public CLICommand
{
public:
DECLARE_PTR_TYPEDEFS(NodeUpdateConfigCommand);
virtual String GetDescription(void) const override;
virtual String GetShortDescription(void) const override;
virtual bool IsDeprecated(void) const override;
virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const override;
virtual ImpersonationLevel GetImpersonationLevel(void) const override;
};
}
#endif /* NODEUPDATECONFIGCOMMAND_H */

View File

@ -1,69 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* 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/repositoryclearchangescommand.hpp"
#include "cli/repositoryutility.hpp"
#include "base/logger.hpp"
#include "base/application.hpp"
#include "base/utility.hpp"
#include <fstream>
#include <iostream>
using namespace icinga;
namespace po = boost::program_options;
REGISTER_CLICOMMAND("repository/clear-changes", RepositoryClearChangesCommand);
String RepositoryClearChangesCommand::GetDescription(void) const
{
return "Clear uncommitted Icinga 2 repository changes";
}
String RepositoryClearChangesCommand::GetShortDescription(void) const
{
return "clear uncommitted repository changes";
}
ImpersonationLevel RepositoryClearChangesCommand::GetImpersonationLevel(void) const
{
return ImpersonateRoot;
}
bool RepositoryClearChangesCommand::IsDeprecated(void) const
{
return true;
}
/**
* The entry point for the "repository clear-changes" CLI command.
*
* @returns An exit status.
*/
int RepositoryClearChangesCommand::Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const
{
if (!Utility::PathExists(RepositoryUtility::GetRepositoryChangeLogPath())) {
std::cout << "Repository Changelog path '" << RepositoryUtility::GetRepositoryChangeLogPath() << "' does not exist. Add objects first!\n";
return 1;
}
std::cout << "Clearing all remaining changes\n";
RepositoryUtility::ClearChangeLog();
return 0;
}

View File

@ -1,50 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* 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 REPOSITORYCLEARCHANGESCOMMAND_H
#define REPOSITORYCLEARCHANGESCOMMAND_H
#include "base/dictionary.hpp"
#include "base/array.hpp"
#include "cli/clicommand.hpp"
#include <ostream>
namespace icinga
{
/**
* The "repository clear-changes" command.
*
* @ingroup cli
*/
class RepositoryClearChangesCommand : public CLICommand
{
public:
DECLARE_PTR_TYPEDEFS(RepositoryClearChangesCommand);
virtual String GetDescription(void) const override;
virtual String GetShortDescription(void) const override;
virtual bool IsDeprecated(void) const override;
virtual ImpersonationLevel GetImpersonationLevel(void) const override;
virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const override;
};
}
#endif /* REPOSITORYCLEARCHANGESCOMMAND_H */

View File

@ -1,85 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* 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/repositorycommitcommand.hpp"
#include "cli/repositoryutility.hpp"
#include "base/logger.hpp"
#include "base/application.hpp"
#include "base/utility.hpp"
#include <fstream>
#include <iostream>
using namespace icinga;
namespace po = boost::program_options;
REGISTER_CLICOMMAND("repository/commit", RepositoryCommitCommand);
String RepositoryCommitCommand::GetDescription(void) const
{
return "Commit Icinga 2 repository changes";
}
String RepositoryCommitCommand::GetShortDescription(void) const
{
return "commit repository changes";
}
void RepositoryCommitCommand::InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const
{
visibleDesc.add_options()
("simulate", "Simulate to-be-committed changes");
}
ImpersonationLevel RepositoryCommitCommand::GetImpersonationLevel(void) const
{
return ImpersonateRoot;
}
bool RepositoryCommitCommand::IsDeprecated(void) const
{
return true;
}
/**
* The entry point for the "repository commit" CLI command.
*
* @returns An exit status.
*/
int RepositoryCommitCommand::Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const
{
if (!Utility::PathExists(RepositoryUtility::GetRepositoryChangeLogPath())) {
std::cout << "Repository Changelog path '" << RepositoryUtility::GetRepositoryChangeLogPath() << "' does not exist. Add objects first!\n";
return 1;
}
if (vm.count("simulate")) {
RepositoryUtility::PrintChangeLog(std::cout);
std::cout << "\n";
std::cout << "Simulation not yet implemented.\n";
//TODO
return 1;
} else {
RepositoryUtility::PrintChangeLog(std::cout);
std::cout << "\n";
RepositoryUtility::CommitChangeLog();
}
return 0;
}

View File

@ -1,52 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* 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 REPOSITORYCOMMITCOMMAND_H
#define REPOSITORYCOMMITCOMMAND_H
#include "base/dictionary.hpp"
#include "base/array.hpp"
#include "cli/clicommand.hpp"
#include <ostream>
namespace icinga
{
/**
* The "repository commit" command.
*
* @ingroup cli
*/
class RepositoryCommitCommand : public CLICommand
{
public:
DECLARE_PTR_TYPEDEFS(RepositoryCommitCommand);
virtual String GetDescription(void) const override;
virtual String GetShortDescription(void) const override;
virtual bool IsDeprecated(void) const override;
virtual void InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const override;
virtual ImpersonationLevel GetImpersonationLevel(void) const override;
virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const override;
};
}
#endif /* REPOSITORYCOMMITCOMMAND_H */

View File

@ -1,204 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* 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/repositoryobjectcommand.hpp"
#include "cli/repositoryutility.hpp"
#include "base/logger.hpp"
#include "base/application.hpp"
#include "base/utility.hpp"
#include <fstream>
#include <iostream>
using namespace icinga;
namespace po = boost::program_options;
REGISTER_REPOSITORY_CLICOMMAND(Host);
REGISTER_REPOSITORY_CLICOMMAND(Service);
REGISTER_REPOSITORY_CLICOMMAND(Zone);
REGISTER_REPOSITORY_CLICOMMAND(Endpoint);
RepositoryObjectCommand::RepositoryObjectCommand(const String& type, RepositoryCommandType command)
: m_Type(type), m_Command(command)
{ }
String RepositoryObjectCommand::GetDescription(void) const
{
String description;
switch (m_Command) {
case RepositoryCommandAdd:
description = "Adds a new";
break;
case RepositoryCommandRemove:
description = "Removes a";
break;
case RepositoryCommandList:
description = "Lists all";
break;
case RepositoryCommandSet:
description = "Set attributes for a";
break;
default:
break;
}
description += " " + m_Type + " object";
if (m_Command == RepositoryCommandList)
description += "s";
return description;
}
String RepositoryObjectCommand::GetShortDescription(void) const
{
String description;
switch (m_Command) {
case RepositoryCommandAdd:
description = "adds a new";
break;
case RepositoryCommandRemove:
description = "removes a";
break;
case RepositoryCommandList:
description = "lists all";
break;
case RepositoryCommandSet:
description = "set attributes for a";
break;
default:
break;
}
description += " " + m_Type + " object";
if (m_Command == RepositoryCommandList)
description += "s";
return description;
}
bool RepositoryObjectCommand::IsDeprecated(void) const
{
return true;
}
void RepositoryObjectCommand::InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const
{
if (m_Command == RepositoryCommandAdd) {
visibleDesc.add_options()
("import", po::value<std::vector<std::string> >(), "Import the defined template into the object. Must be defined and included separately in Icinga 2");
}
}
std::vector<String> RepositoryObjectCommand::GetPositionalSuggestions(const String& word) const
{
if (m_Command == RepositoryCommandAdd) {
Type::Ptr ptype = Type::GetByName(m_Type);
ASSERT(ptype);
return GetFieldCompletionSuggestions(ptype, word);
} else if (m_Command == RepositoryCommandRemove) {
std::vector<String> suggestions;
String argName = "name=";
if (argName.Find(word) == 0)
suggestions.push_back(argName);
if (m_Type == "Service") {
String argHostName = "host_name=";
if (argHostName.Find(word) == 0)
suggestions.push_back(argHostName);
}
return suggestions;
} else
return CLICommand::GetPositionalSuggestions(word);
}
ImpersonationLevel RepositoryObjectCommand::GetImpersonationLevel(void) const
{
return ImpersonateRoot;
}
int RepositoryObjectCommand::GetMaxArguments(void) const
{
return -1;
}
/**
* The entry point for the "repository <type> <add/remove/list>" CLI command.
*
* @returns An exit status.
*/
int RepositoryObjectCommand::Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const
{
Dictionary::Ptr attrs = RepositoryUtility::GetArgumentAttributes(ap);
/* shortcut for list command */
if (m_Command == RepositoryCommandList) {
RepositoryUtility::PrintObjects(std::cout, m_Type);
return 0;
}
if (!attrs->Contains("name")) {
Log(LogCritical, "cli", "Object requires a name (Hint: 'name=<objectname>')!");
return 1;
}
String name = attrs->Get("name");
if (vm.count("import")) {
Array::Ptr imports = new Array();
for (const String& import : vm["import"].as<std::vector<std::string> >()) {
imports->Add(import);
}
//Update object attributes
if (imports->GetLength() > 0)
attrs->Set("import", imports);
}
if (m_Command == RepositoryCommandAdd) {
std::vector<String> object_paths = RepositoryUtility::GetObjects();
Array::Ptr changes = new Array();
RepositoryUtility::GetChangeLog(boost::bind(RepositoryUtility::CollectChange, _1, changes));
RepositoryUtility::AddObject(object_paths, name, m_Type, attrs, changes);
} else if (m_Command == RepositoryCommandRemove) {
Array::Ptr changes = new Array();
RepositoryUtility::GetChangeLog(boost::bind(RepositoryUtility::CollectChange, _1, changes));
/* pass attrs for service->host_name requirement */
RepositoryUtility::RemoveObject(name, m_Type, attrs, changes);
} else if (m_Command == RepositoryCommandSet) {
Log(LogWarning, "cli")
<< "Not supported yet. Please check the roadmap at https://github.com/Icinga/icinga2\n";
return 1;
} else {
Log(LogCritical, "cli")
<< "Invalid command '" << m_Command << "'specified.\n";
return 1;
}
return 0;
}

View File

@ -1,84 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* 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 REPOSITORYOBJECTCOMMAND_H
#define REPOSITORYOBJECTCOMMAND_H
#include "cli/clicommand.hpp"
#include <boost/algorithm/string/case_conv.hpp>
namespace icinga
{
enum RepositoryCommandType
{
RepositoryCommandAdd,
RepositoryCommandRemove,
RepositoryCommandList,
RepositoryCommandSet
};
/**
* The "repository <type> <add/remove/list>" command.
*
* @ingroup cli
*/
class I2_CLI_API RepositoryObjectCommand : public CLICommand
{
public:
DECLARE_PTR_TYPEDEFS(RepositoryObjectCommand);
RepositoryObjectCommand(const String& type, RepositoryCommandType command);
virtual String GetDescription(void) const override;
virtual String GetShortDescription(void) const override;
virtual int GetMaxArguments(void) const override;
virtual bool IsDeprecated(void) const override;
virtual void InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const override;
virtual ImpersonationLevel GetImpersonationLevel(void) const override;
virtual std::vector<String> GetPositionalSuggestions(const String& word) const override;
virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const override;
private:
String m_Type;
RepositoryCommandType m_Command;
};
#define REGISTER_REPOSITORY_CLICOMMAND(type) \
INITIALIZE_ONCE([]() { \
String ltype = #type; \
boost::algorithm::to_lower(ltype); \
\
std::vector<String> name; \
name.push_back("repository"); \
name.push_back(ltype); \
name.push_back("add"); \
CLICommand::Register(name, new RepositoryObjectCommand(#type, RepositoryCommandAdd)); \
\
name[2] = "remove"; \
CLICommand::Register(name, new RepositoryObjectCommand(#type, RepositoryCommandRemove)); \
\
name[2] = "list"; \
CLICommand::Register(name, new RepositoryObjectCommand(#type, RepositoryCommandList)); \
})
}
#endif /* REPOSITORYOBJECTCOMMAND_H */

View File

@ -1,716 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* 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/repositoryutility.hpp"
#include "cli/clicommand.hpp"
#include "base/logger.hpp"
#include "base/application.hpp"
#include "base/convert.hpp"
#include "base/configwriter.hpp"
#include "base/scriptglobal.hpp"
#include "base/json.hpp"
#include "base/netstring.hpp"
#include "base/tlsutility.hpp"
#include "base/stdiostream.hpp"
#include "base/debug.hpp"
#include "base/objectlock.hpp"
#include "base/console.hpp"
#include "base/serializer.hpp"
#include "base/exception.hpp"
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/regex.hpp>
#include <fstream>
#include <iostream>
using namespace icinga;
Dictionary::Ptr RepositoryUtility::GetArgumentAttributes(const std::vector<std::string>& arguments)
{
Dictionary::Ptr attrs = new Dictionary();
for (const String& kv : arguments) {
std::vector<String> tokens;
boost::algorithm::split(tokens, kv, boost::is_any_of("="));
if (tokens.size() != 2) {
Log(LogWarning, "cli")
<< "Cannot parse passed attributes: " << boost::algorithm::join(tokens, "=");
continue;
}
Value value;
try {
value = Convert::ToDouble(tokens[1]);
} catch (...) {
value = tokens[1];
}
attrs->Set(tokens[0], value);
}
return attrs;
}
String RepositoryUtility::GetRepositoryConfigPath(void)
{
return Application::GetSysconfDir() + "/icinga2/repository.d";
}
String RepositoryUtility::GetRepositoryObjectConfigPath(const String& type, const Dictionary::Ptr& object)
{
String path = GetRepositoryConfigPath() + "/";
if (type == "Host")
path += "hosts";
else if (type == "Service")
path += "hosts/" + EscapeName(object->Get("host_name"));
else if (type == "Zone")
path += "zones";
else if (type == "Endpoint")
path += "endpoints";
return path;
}
bool RepositoryUtility::FilterRepositoryObjects(const String& type, const String& path)
{
if (type == "Host") {
boost::regex expr("hosts/[^/]*.conf", boost::regex::icase);
boost::smatch what;
return boost::regex_search(path.GetData(), what, expr);
}
else if (type == "Service")
return Utility::Match("*hosts/*/*.conf", path);
else if (type == "Zone")
return Utility::Match("*zones/*.conf", path);
else if (type == "Endpoints")
return Utility::Match("*endpoints/*.conf", path);
return false;
}
String RepositoryUtility::GetRepositoryObjectConfigFilePath(const String& type, const Dictionary::Ptr& object)
{
String path = GetRepositoryObjectConfigPath(type, object);
path += "/" + EscapeName(object->Get("name")) + ".conf";
return path;
}
String RepositoryUtility::GetRepositoryChangeLogPath(void)
{
return Application::GetLocalStateDir() + "/lib/icinga2/repository/changes";
}
void RepositoryUtility::CreateRepositoryPath(const String& path)
{
if (!Utility::PathExists(path))
Utility::MkDirP(path, 0750);
String user = ScriptGlobal::Get("RunAsUser");
String group = ScriptGlobal::Get("RunAsGroup");
if (!Utility::SetFileOwnership(path, user, group)) {
Log(LogWarning, "cli")
<< "Cannot set ownership for user '" << user << "' group '" << group << "' on path '" << path << "'. Verify it yourself!";
}
}
/* printers */
void RepositoryUtility::PrintObjects(std::ostream& fp, const String& type)
{
std::vector<String> objects = GetObjects(); //full path
for (const String& object : objects) {
if (!FilterRepositoryObjects(type, object)) {
Log(LogDebug, "cli")
<< "Ignoring object '" << object << "'. Type '" << type << "' does not match.";
continue;
}
String file = Utility::BaseName(object);
boost::algorithm::replace_all(file, ".conf", "");
file = UnescapeName(file);
fp << ConsoleColorTag(Console_ForegroundMagenta | Console_Bold) << type << ConsoleColorTag(Console_Normal)
<< " '" << ConsoleColorTag(Console_ForegroundBlue | Console_Bold) << file << ConsoleColorTag(Console_Normal) << "'";
String prefix = Utility::DirName(object);
if (type == "Service") {
std::vector<String> tokens;
boost::algorithm::split(tokens, prefix, boost::is_any_of("/"));
String host_name = UnescapeName(tokens[tokens.size()-1]);
fp << " (on " << ConsoleColorTag(Console_ForegroundMagenta | Console_Bold) << "Host" << ConsoleColorTag(Console_Normal)
<< " '" << ConsoleColorTag(Console_ForegroundBlue | Console_Bold) << host_name << ConsoleColorTag(Console_Normal) << "')";
}
fp << "\n";
}
}
void RepositoryUtility::PrintChangeLog(std::ostream& fp)
{
Array::Ptr changelog = new Array();
GetChangeLog(boost::bind(RepositoryUtility::CollectChange, _1, changelog));
ObjectLock olock(changelog);
std::cout << "Changes to be committed:\n\n";
for (const Value& entry : changelog) {
FormatChangelogEntry(std::cout, entry);
}
}
class RepositoryValidationUtils : public ValidationUtils
{
public:
virtual bool ValidateName(const String& type, const String& name) const
{
return true;
}
};
/* modify objects and write changelog */
bool RepositoryUtility::AddObject(const std::vector<String>& object_paths, const String& name, const String& type,
const Dictionary::Ptr& attrs, const Array::Ptr& changes, bool check_config)
{
String pattern;
if (type == "Service")
pattern = EscapeName(attrs->Get("host_name")) + "/" + EscapeName(name) + ".conf";
else
pattern = EscapeName(name) + ".conf";
for (const String& object_path : object_paths) {
if (object_path.Contains(pattern)) {
Log(LogWarning, "cli")
<< type << " '" << name << "' already exists. Skipping creation.";
return false;
}
}
/* add a new changelog entry by timestamp */
String path = GetRepositoryChangeLogPath() + "/" + Convert::ToString(Utility::GetTime()) + "-" + type + "-" + SHA256(name) + ".change";
Dictionary::Ptr change = new Dictionary();
change->Set("timestamp", Utility::GetTime());
change->Set("name", name);
change->Set("type", type);
change->Set("command", "add");
change->Set("attrs", attrs);
Type::Ptr utype = Type::GetByName(type);
ASSERT(utype);
if (check_config) {
try {
ConfigObject::Ptr object = static_pointer_cast<ConfigObject>(utype->Instantiate(std::vector<Value>()));
/* temporarly set the object type for validation */
attrs->Set("type", utype->GetName());
Deserialize(object, attrs, false, FAConfig);
object->SetName(name);
RepositoryValidationUtils utils;
static_pointer_cast<ConfigObject>(object)->Validate(FAConfig, utils);
attrs->Remove("type");
} catch (const ValidationError& ex) {
Log(LogCritical, "config", DiagnosticInformation(ex));
return false;
}
}
if (CheckChangeExists(change, changes)) {
Log(LogWarning, "cli")
<< "Change '" << change->Get("command") << "' for type '"
<< change->Get("type") << "' and name '" << change->Get("name")
<< "' already exists.";
return false;
}
/* store the cached change */
changes->Add(change);
return WriteObjectToRepositoryChangeLog(path, change);
}
bool RepositoryUtility::RemoveObject(const String& name, const String& type, const Dictionary::Ptr& attrs, const Array::Ptr& changes)
{
/* add a new changelog entry by timestamp */
String path = GetRepositoryChangeLogPath() + "/" + Convert::ToString(Utility::GetTime()) + "-" + type + "-" + SHA256(name) + ".change";
Dictionary::Ptr change = new Dictionary();
change->Set("timestamp", Utility::GetTime());
change->Set("name", name);
change->Set("type", type);
change->Set("command", "remove");
change->Set("attrs", attrs); //required for service->host_name
if (CheckChangeExists(change, changes)) {
Log(LogWarning, "cli")
<< "Change '" << change->Get("command") << "' for type '"
<< change->Get("type") << "' and name '" << change->Get("name")
<< "' already exists.";
return false;
}
/* store the cached change */
changes->Add(change);
return WriteObjectToRepositoryChangeLog(path, change);
}
bool RepositoryUtility::SetObjectAttribute(const String& name, const String& type, const String& attr, const Value& val)
{
//TODO: Implement modification commands
return true;
}
bool RepositoryUtility::CheckChangeExists(const Dictionary::Ptr& change, const Array::Ptr& changes)
{
Dictionary::Ptr attrs = change->Get("attrs");
ObjectLock olock(changes);
for (const Dictionary::Ptr& entry : changes) {
if (entry->Get("type") != change->Get("type"))
continue;
if (entry->Get("name") != change->Get("name"))
continue;
Dictionary::Ptr their_attrs = entry->Get("attrs");
if (entry->Get("type") == "Service" && attrs->Get("host_name") != their_attrs->Get("host_name"))
continue;
if (entry->Get("command") != change->Get("command"))
continue;
/* only works for add/remove commands (no set) */
if (change->Get("command") == "add" || change->Get("command") == "remove")
return true;
}
return false;
}
bool RepositoryUtility::ClearChangeLog(void)
{
GetChangeLog(boost::bind(RepositoryUtility::ClearChange, _1, _2));
return true;
}
bool RepositoryUtility::ChangeLogHasPendingChanges(void)
{
Array::Ptr changelog = new Array();
GetChangeLog(boost::bind(RepositoryUtility::CollectChange, _1, changelog));
return changelog->GetLength() > 0;
}
/* commit changelog */
bool RepositoryUtility::CommitChangeLog(void)
{
GetChangeLog(boost::bind(RepositoryUtility::CommitChange, _1, _2));
return true;
}
/* write/read from changelog repository */
bool RepositoryUtility::WriteObjectToRepositoryChangeLog(const String& path, const Dictionary::Ptr& item)
{
Log(LogInformation, "cli", "Dumping changelog items to file '" + path + "'");
CreateRepositoryPath(Utility::DirName(path));
std::fstream fp;
String tempFilename = Utility::CreateTempFile(path + ".XXXXXX", 0600, fp);
fp << JsonEncode(item);
fp.close();
#ifdef _WIN32
_unlink(path.CStr());
#endif /* _WIN32 */
if (rename(tempFilename.CStr(), path.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 RepositoryUtility::GetObjectFromRepositoryChangeLog(const String& filename)
{
std::fstream fp;
fp.open(filename.CStr(), std::ifstream::in);
if (!fp)
return Dictionary::Ptr();
String content((std::istreambuf_iterator<char>(fp)), std::istreambuf_iterator<char>());
fp.close();
return JsonDecode(content);
}
/* internal implementation when changes are committed */
bool RepositoryUtility::AddObjectInternal(const String& name, const String& type, const Dictionary::Ptr& attrs)
{
String path = GetRepositoryObjectConfigPath(type, attrs) + "/" + EscapeName(name) + ".conf";
return WriteObjectToRepository(path, name, type, attrs);
}
bool RepositoryUtility::RemoveObjectInternal(const String& name, const String& type, const Dictionary::Ptr& attrs)
{
String path = GetRepositoryObjectConfigPath(type, attrs) + "/" + EscapeName(name) + ".conf";
if (!Utility::PathExists(path)) {
Log(LogWarning, "cli")
<< type << " '" << name << "' does not exist.";
return true;
}
bool success = RemoveObjectFileInternal(path);
if (success)
Log(LogInformation, "cli")
<< "Removing config object '" << name << "' in file '" << path << "'";
/* special treatment for hosts -> remove the services too */
if (type == "Host") {
path = GetRepositoryObjectConfigPath(type, attrs) + "/" + name;
/* if path does not exist, this host does not have any services */
if (!Utility::PathExists(path)) {
Log(LogNotice, "cli")
<< type << " '" << name << "' does not have any services configured.";
return success;
}
std::vector<String> files;
Utility::GlobRecursive(path, "*.conf",
boost::bind(&RepositoryUtility::CollectObjects, _1, boost::ref(files)), GlobFile);
for (const String& file : files) {
RemoveObjectFileInternal(file);
}
#ifndef _WIN32
rmdir(path.CStr());
#else
_rmdir(path.CStr());
#endif /* _WIN32 */
}
return success;
}
bool RepositoryUtility::RemoveObjectFileInternal(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 path '" + path +
"'. Failed with error code " + Convert::ToString(errno) + ", \"" + Utility::FormatErrorNumber(errno) + "\".");
return false;
}
return true;
}
bool RepositoryUtility::SetObjectAttributeInternal(const String& name, const String& type, const String& key, const Value& val, const Dictionary::Ptr& attrs)
{
//TODO
String path = GetRepositoryObjectConfigPath(type, attrs) + "/" + EscapeName(name) + ".conf";
Dictionary::Ptr obj = GetObjectFromRepository(path); //TODO
if (!obj) {
Log(LogCritical, "cli")
<< "Can't get object " << name << " from repository.\n";
return false;
}
obj->Set(key, val);
std::cout << "Writing object '" << name << "' to path '" << path << "'.\n";
//TODO: Create a patch file
if (!WriteObjectToRepository(path, name, type, obj)) {
Log(LogCritical, "cli")
<< "Can't write object " << name << " to repository.\n";
return false;
}
return true;
}
bool RepositoryUtility::WriteObjectToRepository(const String& path, const String& name, const String& type, const Dictionary::Ptr& item)
{
Log(LogInformation, "cli")
<< "Writing config object '" << name << "' to file '" << path << "'";
CreateRepositoryPath(Utility::DirName(path));
std::fstream fp;
String tempFilename = Utility::CreateTempFile(path + ".XXXXXX", 0644, fp);
SerializeObject(fp, name, type, item);
fp << std::endl;
fp.close();
#ifdef _WIN32
_unlink(path.CStr());
#endif /* _WIN32 */
if (rename(tempFilename.CStr(), path.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 RepositoryUtility::GetObjectFromRepository(const String& filename)
{
//TODO: Parse existing configuration objects
return Dictionary::Ptr();
}
String RepositoryUtility::EscapeName(const String& name)
{
return Utility::EscapeString(name, "<>:\"/\\|?*", true);
}
String RepositoryUtility::UnescapeName(const String& name)
{
return Utility::UnescapeString(name);
}
/*
* collect functions
*/
std::vector<String> RepositoryUtility::GetObjects(void)
{
std::vector<String> objects;
String path = GetRepositoryConfigPath();
Utility::GlobRecursive(path, "*.conf",
boost::bind(&RepositoryUtility::CollectObjects, _1, boost::ref(objects)), GlobFile);
return objects;
}
void RepositoryUtility::CollectObjects(const String& object_file, std::vector<String>& objects)
{
Log(LogDebug, "cli")
<< "Adding object: '" << object_file << "'.";
objects.push_back(object_file);
}
bool RepositoryUtility::GetChangeLog(const boost::function<void (const Dictionary::Ptr&, const String&)>& callback)
{
std::vector<String> changelog;
String path = GetRepositoryChangeLogPath() + "/";
Utility::MkDirP(path, 0700);
Utility::Glob(path + "/*.change",
boost::bind(&RepositoryUtility::CollectChangeLog, _1, boost::ref(changelog)), GlobFile);
/* sort by timestamp ascending */
std::sort(changelog.begin(), changelog.end());
for (const String& entry : changelog) {
String file = path + entry + ".change";
Dictionary::Ptr change = GetObjectFromRepositoryChangeLog(file);
Log(LogDebug, "cli")
<< "Collecting entry " << entry << "\n";
if (change)
callback(change, file);
}
return true;
}
void RepositoryUtility::CollectChangeLog(const String& change_file, std::vector<String>& changelog)
{
String file = Utility::BaseName(change_file);
boost::algorithm::replace_all(file, ".change", "");
Log(LogDebug, "cli")
<< "Adding change file: '" << file << "'.";
changelog.push_back(file);
}
void RepositoryUtility::CollectChange(const Dictionary::Ptr& change, Array::Ptr& changes)
{
changes->Add(change);
}
/*
* Commit Changelog entry
*/
void RepositoryUtility::CommitChange(const Dictionary::Ptr& change, const String& path)
{
Log(LogDebug, "cli")
<< "Got change " << change->Get("name");
String name = change->Get("name");
String type = change->Get("type");
String command = change->Get("command");
Dictionary::Ptr attrs;
if (change->Contains("attrs"))
attrs = change->Get("attrs");
bool success = false;
if (command == "add")
success = AddObjectInternal(name, type, attrs);
else if (command == "remove")
success = RemoveObjectInternal(name, type, attrs);
if (success) {
Log(LogNotice, "cli")
<< "Removing changelog file '" << path << "'.";
RemoveObjectFileInternal(path);
}
}
/*
* Clear Changelog entry
*/
void RepositoryUtility::ClearChange(const Dictionary::Ptr& change, const String& path)
{
Log(LogDebug, "cli")
<< "Clearing change " << change->Get("name");
Log(LogInformation, "cli")
<< "Removing changelog file '" << path << "'.";
RemoveObjectFileInternal(path);
}
/*
* Print Changelog helpers
*/
void RepositoryUtility::FormatChangelogEntry(std::ostream& fp, const Dictionary::Ptr& change)
{
if (!change)
return;
if (change->Get("command") == "add")
fp << "Adding";
if (change->Get("command") == "remove")
fp << "Removing";
String type = change->Get("type");
boost::algorithm::to_lower(type);
Dictionary::Ptr attrs = change->Get("attrs");
fp << " " << ConsoleColorTag(Console_ForegroundMagenta | Console_Bold) << type << ConsoleColorTag(Console_Normal) << " '";
fp << ConsoleColorTag(Console_ForegroundBlue | Console_Bold) << change->Get("name") << ConsoleColorTag(Console_Normal) << "'\n";
ObjectLock olock(attrs);
for (const Dictionary::Pair& kv : attrs) {
/* skip the name */
if (kv.first == "name" || kv.first == "__name")
continue;
fp << std::setw(4) << " " << ConsoleColorTag(Console_ForegroundGreen) << kv.first << ConsoleColorTag(Console_Normal) << " = ";
ConfigWriter::EmitValue(fp, 0, kv.second);
fp << "\n";
}
}
/*
* print helpers for configuration
* TODO: Move into a separate class
*/
void RepositoryUtility::SerializeObject(std::ostream& fp, const String& name, const String& type, const Dictionary::Ptr& object)
{
fp << "object " << type << " ";
ConfigWriter::EmitString(fp, name);
fp << " {\n";
if (!object) {
fp << "}\n";
return;
}
if (object->Contains("import")) {
Array::Ptr imports = object->Get("import");
ObjectLock olock(imports);
for (const String& import : imports) {
fp << "\t" << "import ";
ConfigWriter::EmitString(fp, import);
fp << '\n';
}
}
ObjectLock xlock(object);
for (const Dictionary::Pair& kv : object) {
if (kv.first == "import" || kv.first == "name" || kv.first == "__name") {
continue;
} else {
fp << "\t";
ConfigWriter::EmitIdentifier(fp, kv.first, true);
fp << " = ";
ConfigWriter::EmitValue(fp, 1, kv.second);
}
fp << "\n";
}
fp << "}\n";
}

View File

@ -1,108 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* 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 REPOSITORYUTILITY_H
#define REPOSITORYUTILITY_H
#include "base/i2-base.hpp"
#include "cli/i2-cli.hpp"
#include "base/dictionary.hpp"
#include "base/array.hpp"
#include "base/value.hpp"
#include "base/string.hpp"
#include <boost/function.hpp>
namespace icinga
{
/**
* @ingroup cli
*/
class I2_CLI_API RepositoryUtility
{
public:
static Dictionary::Ptr GetArgumentAttributes(const std::vector<std::string>& arguments);
static String GetRepositoryConfigPath(void);
static String GetRepositoryObjectConfigPath(const String& type, const Dictionary::Ptr& object);
static String GetRepositoryObjectConfigFilePath(const String& type, const Dictionary::Ptr& object);
static String GetRepositoryChangeLogPath(void);
static void CreateRepositoryPath(const String& path);
static bool FilterRepositoryObjects(const String& type, const String& path);
static void PrintObjects(std::ostream& fp, const String& type);
static void PrintChangeLog(std::ostream& fp);
static bool AddObject(const std::vector<String>& object_paths, const String& name, const String& type, const Dictionary::Ptr& attrs,
const Array::Ptr& changes, bool check_config = true);
static bool RemoveObject(const String& name, const String& type, const Dictionary::Ptr& attrs, const Array::Ptr& changes);
static bool CheckChangeExists(const Dictionary::Ptr& change, const Array::Ptr& changes);
static bool SetObjectAttribute(const String& name, const String& type, const String& attr, const Value& val);
static bool CommitChangeLog(void);
static bool ClearChangeLog(void);
static bool ChangeLogHasPendingChanges(void);
static bool GetChangeLog(const boost::function<void (const Dictionary::Ptr&, const String&)>& callback);
static void CollectChangeLog(const String& change_file, std::vector<String>& changelog);
static void CollectChange(const Dictionary::Ptr& change, Array::Ptr& changes);
static std::vector<String> GetObjects(void);
static void CollectObjects(const String& object_file, std::vector<String>& objects);
private:
RepositoryUtility(void);
static bool RemoveObjectFileInternal(const String& path);
static bool AddObjectInternal(const String& name, const String& type, const Dictionary::Ptr& attrs);
static bool RemoveObjectInternal(const String& name, const String& type, const Dictionary::Ptr& attrs);
static bool SetObjectAttributeInternal(const String& name, const String& type, const String& key,
const Value& val, const Dictionary::Ptr& attrs);
/* repository.d */
static bool WriteObjectToRepository(const String& path, const String& name, const String& type, const Dictionary::Ptr& item);
static Dictionary::Ptr GetObjectFromRepository(const String& filename);
/* changelog */
static bool WriteObjectToRepositoryChangeLog(const String& path, const Dictionary::Ptr& item);
static Dictionary::Ptr GetObjectFromRepositoryChangeLog(const String& filename);
static void CommitChange(const Dictionary::Ptr& change, const String& path);
static void ClearChange(const Dictionary::Ptr& change, const String& path);
static void FormatChangelogEntry(std::ostream& fp, const Dictionary::Ptr& change);
/* config print helpers */
static void SerializeObject(std::ostream& fp, const String& name, const String& type, const Dictionary::Ptr& object);
static void FormatValue(std::ostream& fp, const Value& val);
static void FormatArray(std::ostream& fp, const Array::Ptr& arr);
static String EscapeName(const String& name);
static String UnescapeName(const String& name);
};
}
#endif /* REPOSITORYUTILITY_H */

View File

@ -66,7 +66,6 @@ endif()
#install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/api\")")
install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/api/log\")")
install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/api/repository\")")
install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/api/zones\")")