Implement config object sync

Adds object version.

refs #9927
This commit is contained in:
Gunnar Beutner 2015-08-20 16:43:03 +02:00 committed by Michael Friedrich
parent 1d1d2ceb96
commit 6fa58a520c
32 changed files with 366 additions and 220 deletions

View File

@ -321,7 +321,7 @@ int Main(void)
std::cout << appName << " " << "- The Icinga 2 network monitoring daemon (version: "
<< ConsoleColorTag(vm.count("version") ? Console_ForegroundRed : Console_Normal)
<< Application::GetVersion()
<< Application::GetAppVersion()
#ifdef I2_DEBUG
<< "; debug"
#endif /* I2_DEBUG */
@ -384,10 +384,10 @@ int Main(void)
Logger::DisableTimestamp(true);
#ifndef _WIN32
if (command->GetImpersonationLevel() == ImpersonateRoot) {
if (getuid() != 0) {
/*if (getuid() != 0) {
Log(LogCritical, "cli", "This command must be run as root.");
return 0;
}
}*/
} else if (command && command->GetImpersonationLevel() == ImpersonateIcinga) {
String group = Application::GetRunAsGroup();
String user = Application::GetRunAsUser();

View File

@ -22,7 +22,7 @@
using namespace icinga;
String Application::GetVersion(void)
String Application::GetAppVersion(void)
{
return VERSION;
}

View File

@ -591,7 +591,7 @@ void Application::DisplayInfoMessage(std::ostream& os, bool skipVersion)
os << "Application information:" << "\n";
if (!skipVersion)
os << " Application version: " << GetVersion() << "\n";
os << " Application version: " << GetAppVersion() << "\n";
os << " Installation root: " << GetPrefixDir() << "\n"
<< " Sysconf directory: " << GetSysconfDir() << "\n"

View File

@ -129,7 +129,7 @@ public:
static ThreadPool& GetTP(void);
static String GetVersion(void);
static String GetAppVersion(void);
static double GetStartTime(void);
static void SetStartTime(double ts);

View File

@ -184,6 +184,7 @@ void ConfigObject::ModifyAttribute(const String& attr, const Value& value)
ValidateField(fid, newValue, utils);
SetField(fid, newValue);
SetVersion(GetVersion() + 1);
if (updated_original_attributes)
NotifyOriginalAttributes();

View File

@ -94,6 +94,9 @@ abstract class ConfigObject : ConfigObjectBase
[protected] bool state_loaded;
Dictionary::Ptr original_attributes;
[state] int version {
default {{{ return 1; }}}
};
};
}

View File

@ -186,7 +186,7 @@ int ConsoleCommand::Run(const po::variables_map& vm, const std::vector<std::stri
scriptFrame.Sandboxed = true;
}
std::cout << "Icinga (version: " << Application::GetVersion() << ")\n";
std::cout << "Icinga (version: " << Application::GetAppVersion() << ")\n";
while (std::cin.good()) {
String fileName = "<" + Convert::ToString(next_line) + ">";

View File

@ -209,7 +209,7 @@ int DaemonCommand::Run(const po::variables_map& vm, const std::vector<std::strin
Logger::DisableTimestamp(false);
Log(LogInformation, "cli")
<< "Icinga application loader (version: " << Application::GetVersion()
<< "Icinga application loader (version: " << Application::GetAppVersion()
#ifdef I2_DEBUG
<< "; debug"
#endif /* I2_DEBUG */

View File

@ -148,7 +148,7 @@ bool TroubleshootCommand::GeneralInfo(InfoLog& log, const boost::program_options
//Application::DisplayInfoMessage() but formatted
InfoLogLine(log)
<< "\tApplication version: " << Application::GetVersion() << '\n'
<< "\tApplication version: " << Application::GetAppVersion() << '\n'
<< "\tInstallation root: " << Application::GetPrefixDir() << '\n'
<< "\tSysconf directory: " << Application::GetSysconfDir() << '\n'
<< "\tRun directory: " << Application::GetRunDir() << '\n'

View File

@ -800,7 +800,7 @@ void StatusDataWriter::StatusTimerHandler(void)
statusfp << "info {" "\n"
"\t" "created=" << Utility::GetTime() << "\n"
"\t" "version=" << Application::GetVersion() << "\n"
"\t" "version=" << Application::GetAppVersion() << "\n"
"\t" "}" "\n"
"\n";

View File

@ -115,6 +115,11 @@ Dictionary::Ptr ConfigItem::GetScope(void) const
return m_Scope;
}
ConfigObject::Ptr ConfigItem::GetObject(void) const
{
return m_Object;
}
/**
* Retrieves the expression list for the configuration item.
*
@ -140,7 +145,7 @@ class DefaultValidationUtils : public ValidationUtils
public:
virtual bool ValidateName(const String& type, const String& name) const override
{
return ConfigItem::GetObject(type, name) != ConfigItem::Ptr();
return ConfigItem::GetByTypeAndName(type, name) != ConfigItem::Ptr();
}
};
@ -302,7 +307,7 @@ void ConfigItem::Unregister(void)
* @param name The name of the ConfigItem that is to be looked up.
* @returns The configuration item.
*/
ConfigItem::Ptr ConfigItem::GetObject(const String& type, const String& name)
ConfigItem::Ptr ConfigItem::GetByTypeAndName(const String& type, const String& name)
{
boost::mutex::scoped_lock lock(m_Mutex);

View File

@ -61,7 +61,9 @@ public:
DebugInfo GetDebugInfo(void) const;
Dictionary::Ptr GetScope(void) const;
static ConfigItem::Ptr GetObject(const String& type,
ConfigObject::Ptr GetObject(void) const;
static ConfigItem::Ptr GetByTypeAndName(const String& type,
const String& name);
static bool CommitItems(WorkQueue& upq);

View File

@ -30,38 +30,34 @@
using namespace icinga;
ConfigWriter::ConfigWriter(const String& fileName)
: m_FP(fileName.CStr(), std::ofstream::out | std::ostream::trunc)
{ }
void ConfigWriter::EmitBoolean(bool val)
void ConfigWriter::EmitBoolean(std::ostream& fp, bool val)
{
m_FP << (val ? "true" : "false");
fp << (val ? "true" : "false");
}
void ConfigWriter::EmitNumber(double val)
void ConfigWriter::EmitNumber(std::ostream& fp, double val)
{
m_FP << val;
fp << val;
}
void ConfigWriter::EmitString(const String& val)
void ConfigWriter::EmitString(std::ostream& fp, const String& val)
{
m_FP << "\"" << EscapeIcingaString(val) << "\"";
fp << "\"" << EscapeIcingaString(val) << "\"";
}
void ConfigWriter::EmitEmpty(void)
void ConfigWriter::EmitEmpty(std::ostream& fp)
{
m_FP << "null";
fp << "null";
}
void ConfigWriter::EmitArray(const Array::Ptr& val)
void ConfigWriter::EmitArray(std::ostream& fp, const Array::Ptr& val)
{
m_FP << "[ ";
EmitArrayItems(val);
m_FP << " ]";
fp << "[ ";
EmitArrayItems(fp, val);
fp << " ]";
}
void ConfigWriter::EmitArrayItems(const Array::Ptr& val)
void ConfigWriter::EmitArrayItems(std::ostream& fp, const Array::Ptr& val)
{
bool first = true;
@ -70,80 +66,82 @@ void ConfigWriter::EmitArrayItems(const Array::Ptr& val)
if (first)
first = false;
else
m_FP << ", ";
fp << ", ";
EmitValue(0, item);
EmitValue(fp, 0, item);
}
}
void ConfigWriter::EmitScope(int indentLevel, const Dictionary::Ptr& val, const Array::Ptr& imports)
void ConfigWriter::EmitScope(std::ostream& fp, int indentLevel, const Dictionary::Ptr& val, const Array::Ptr& imports)
{
m_FP << "{";
fp << "{";
if (imports && imports->GetLength() > 0) {
ObjectLock xlock(imports);
BOOST_FOREACH(const Value& import, imports) {
m_FP << "\n";
EmitIndent(indentLevel);
m_FP << "import \"" << import << "\"";
fp << "\n";
EmitIndent(fp, indentLevel);
fp << "import \"" << import << "\"";
}
m_FP << "\n";
fp << "\n";
}
ObjectLock olock(val);
BOOST_FOREACH(const Dictionary::Pair& kv, val) {
m_FP << "\n";
EmitIndent(indentLevel);
std::vector<String> tokens;
boost::algorithm::split(tokens, kv.first, boost::is_any_of("."));
EmitIdentifier(tokens[0], true);
for (std::vector<String>::size_type i = 1; i < tokens.size(); i++) {
m_FP << "[";
EmitString(tokens[i]);
m_FP << "]";
if (val) {
ObjectLock olock(val);
BOOST_FOREACH(const Dictionary::Pair& kv, val) {
fp << "\n";
EmitIndent(fp, indentLevel);
std::vector<String> tokens;
boost::algorithm::split(tokens, kv.first, boost::is_any_of("."));
EmitIdentifier(fp, tokens[0], true);
for (std::vector<String>::size_type i = 1; i < tokens.size(); i++) {
fp << "[";
EmitString(fp, tokens[i]);
fp << "]";
}
fp << " = ";
EmitValue(fp, indentLevel + 1, kv.second);
}
m_FP << " = ";
EmitValue(indentLevel + 1, kv.second);
}
m_FP << "\n";
EmitIndent(indentLevel - 1);
m_FP << "}";
fp << "\n";
EmitIndent(fp, indentLevel - 1);
fp << "}";
}
void ConfigWriter::EmitValue(int indentLevel, const Value& val)
void ConfigWriter::EmitValue(std::ostream& fp, int indentLevel, const Value& val)
{
if (val.IsObjectType<Array>())
EmitArray(val);
EmitArray(fp, val);
else if (val.IsObjectType<Dictionary>())
EmitScope(indentLevel, val);
EmitScope(fp, indentLevel, val);
else if (val.IsString())
EmitString(val);
EmitString(fp, val);
else if (val.IsNumber())
EmitNumber(val);
EmitNumber(fp, val);
else if (val.IsBoolean())
EmitBoolean(val);
EmitBoolean(fp, val);
else if (val.IsEmpty())
EmitEmpty();
EmitEmpty(fp);
}
void ConfigWriter::EmitRaw(const String& val)
void ConfigWriter::EmitRaw(std::ostream& fp, const String& val)
{
m_FP << val;
fp << val;
}
void ConfigWriter::EmitIndent(int indentLevel)
void ConfigWriter::EmitIndent(std::ostream& fp, int indentLevel)
{
for (int i = 0; i < indentLevel; i++)
m_FP << "\t";
fp << "\t";
}
void ConfigWriter::EmitIdentifier(const String& identifier, bool inAssignment)
void ConfigWriter::EmitIdentifier(std::ostream& fp, const String& identifier, bool inAssignment)
{
static std::set<String> keywords;
if (keywords.empty()) {
@ -152,46 +150,46 @@ void ConfigWriter::EmitIdentifier(const String& identifier, bool inAssignment)
}
if (keywords.find(identifier) != keywords.end()) {
m_FP << "@" << identifier;
fp << "@" << identifier;
return;
}
boost::regex expr("^[a-zA-Z_][a-zA-Z0-9\\_]*$");
boost::smatch what;
if (boost::regex_search(identifier.GetData(), what, expr))
m_FP << identifier;
fp << identifier;
else if (inAssignment)
EmitString(identifier);
EmitString(fp, identifier);
else
BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid identifier"));
}
void ConfigWriter::EmitConfigItem(const String& type, const String& name, bool isTemplate,
void ConfigWriter::EmitConfigItem(std::ostream& fp, const String& type, const String& name, bool isTemplate,
const Array::Ptr& imports, const Dictionary::Ptr& attrs)
{
if (isTemplate)
m_FP << "template ";
fp << "template ";
else
m_FP << "object ";
fp << "object ";
EmitIdentifier(type, false);
m_FP << " ";
EmitString(name);
m_FP << " ";
EmitScope(1, attrs, imports);
EmitIdentifier(fp, type, false);
fp << " ";
EmitString(fp, name);
fp << " ";
EmitScope(fp, 1, attrs, imports);
}
void ConfigWriter::EmitComment(const String& text)
void ConfigWriter::EmitComment(std::ostream& fp, const String& text)
{
m_FP << "/* " << text << " */\n";
fp << "/* " << text << " */\n";
}
void ConfigWriter::EmitFunctionCall(const String& name, const Array::Ptr& arguments)
void ConfigWriter::EmitFunctionCall(std::ostream& fp, const String& name, const Array::Ptr& arguments)
{
EmitIdentifier(name, false);
m_FP << "(";
EmitArrayItems(arguments);
m_FP << ")";
EmitIdentifier(fp, name, false);
fp << "(";
EmitArrayItems(fp, arguments);
fp << ")";
}
String ConfigWriter::EscapeIcingaString(const String& str)

View File

@ -34,34 +34,30 @@ namespace icinga
*
* @ingroup config
*/
class I2_CONFIG_API ConfigWriter : public Object
class I2_CONFIG_API ConfigWriter
{
public:
DECLARE_PTR_TYPEDEFS(ConfigWriter);
static void EmitBoolean(std::ostream& fp, bool val);
static void EmitNumber(std::ostream& fp, double val);
static void EmitString(std::ostream& fp, const String& val);
static void EmitEmpty(std::ostream& fp);
static void EmitArray(std::ostream& fp, const Array::Ptr& val);
static void EmitArrayItems(std::ostream& fp, const Array::Ptr& val);
static void EmitDictionary(std::ostream& fp, const Dictionary::Ptr& val);
static void EmitScope(std::ostream& fp, int indentLevel, const Dictionary::Ptr& val, const Array::Ptr& imports = Array::Ptr());
static void EmitValue(std::ostream& fp, int indentLevel, const Value& val);
static void EmitRaw(std::ostream& fp, const String& val);
static void EmitIndent(std::ostream& fp, int indentLevel);
ConfigWriter(const String& fileName);
void EmitBoolean(bool val);
void EmitNumber(double val);
void EmitString(const String& val);
void EmitEmpty(void);
void EmitArray(const Array::Ptr& val);
void EmitArrayItems(const Array::Ptr& val);
void EmitDictionary(const Dictionary::Ptr& val);
void EmitScope(int indentLevel, const Dictionary::Ptr& val, const Array::Ptr& imports = Array::Ptr());
void EmitValue(int indentLevel, const Value& val);
void EmitRaw(const String& val);
void EmitIndent(int indentLevel);
void EmitIdentifier(const String& identifier, bool inAssignment);
void EmitConfigItem(const String& type, const String& name, bool isTemplate,
static void EmitIdentifier(std::ostream& fp, const String& identifier, bool inAssignment);
static void EmitConfigItem(std::ostream& fp, const String& type, const String& name, bool isTemplate,
const Array::Ptr& imports, const Dictionary::Ptr& attrs);
void EmitComment(const String& text);
void EmitFunctionCall(const String& name, const Array::Ptr& arguments);
static void EmitComment(std::ostream& fp, const String& text);
static void EmitFunctionCall(std::ostream& fp, const String& name, const Array::Ptr& arguments);
private:
std::ofstream m_FP;
ConfigWriter(void);
static String EscapeIcingaString(const String& str);
};

View File

@ -724,7 +724,7 @@ ExpressionResult ImportExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhi
if (!name.IsString())
BOOST_THROW_EXCEPTION(ScriptError("Template/object name must be a string", m_DebugInfo));
ConfigItem::Ptr item = ConfigItem::GetObject(type, name);
ConfigItem::Ptr item = ConfigItem::GetByTypeAndName(type, name);
if (!item)
BOOST_THROW_EXCEPTION(ScriptError("Import references unknown template: '" + name + "'", m_DebugInfo));

View File

@ -131,7 +131,7 @@ public:
}
if (!checkName.IsEmpty()) {
ConfigItem::Ptr oldItem = ConfigItem::GetObject(type, checkName);
ConfigItem::Ptr oldItem = ConfigItem::GetByTypeAndName(type, checkName);
if (oldItem) {
std::ostringstream msgbuf;

View File

@ -141,7 +141,7 @@ void DbConnection::ProgramStatusHandler(void)
query2.Fields = new Dictionary();
query2.Fields->Set("instance_id", 0); /* DbConnection class fills in real ID */
query2.Fields->Set("program_version", Application::GetVersion());
query2.Fields->Set("program_version", Application::GetAppVersion());
query2.Fields->Set("status_update_time", DbValue::FromTimestamp(Utility::GetTime()));
query2.Fields->Set("program_start_time", DbValue::FromTimestamp(Application::GetStartTime()));
query2.Fields->Set("is_currently_running", 1);

View File

@ -331,7 +331,7 @@ void IdoMysqlConnection::Reconnect(void)
/* record connection */
Query("INSERT INTO " + GetTablePrefix() + "conninfo " +
"(instance_id, connect_time, last_checkin_time, agent_name, agent_version, connect_type, data_start_time) VALUES ("
+ Convert::ToString(static_cast<long>(m_InstanceID)) + ", NOW(), NOW(), 'icinga2 db_ido_mysql', '" + Escape(Application::GetVersion())
+ Convert::ToString(static_cast<long>(m_InstanceID)) + ", NOW(), NOW(), 'icinga2 db_ido_mysql', '" + Escape(Application::GetAppVersion())
+ "', '" + (reconnect ? "RECONNECT" : "INITIAL") + "', NOW())");
/* clear config tables for the initial config dump */

View File

@ -323,7 +323,7 @@ void IdoPgsqlConnection::Reconnect(void)
/* record connection */
Query("INSERT INTO " + GetTablePrefix() + "conninfo " +
"(instance_id, connect_time, last_checkin_time, agent_name, agent_version, connect_type, data_start_time) VALUES ("
+ Convert::ToString(static_cast<long>(m_InstanceID)) + ", NOW(), NOW(), E'icinga2 db_ido_pgsql', E'" + Escape(Application::GetVersion())
+ Convert::ToString(static_cast<long>(m_InstanceID)) + ", NOW(), NOW(), E'icinga2 db_ido_pgsql', E'" + Escape(Application::GetAppVersion())
+ "', E'" + (reconnect ? "RECONNECT" : "INITIAL") + "', NOW())");
/* clear config tables for the initial config dump */

View File

@ -81,7 +81,7 @@ void IcingaApplication::StatsFunc(const Dictionary::Ptr& status, const Array::Pt
stats->Set("enable_perfdata", icingaapplication->GetEnablePerfdata());
stats->Set("pid", Utility::GetPid());
stats->Set("program_start", Application::GetStartTime());
stats->Set("version", Application::GetVersion());
stats->Set("version", Application::GetAppVersion());
nodes->Set(icingaapplication->GetName(), stats);
}
@ -139,30 +139,33 @@ void IcingaApplication::OnShutdown(void)
DumpProgramState();
}
static void PersistModAttrHelper(const ConfigWriter::Ptr& cw, ConfigObject::Ptr& previousObject, const ConfigObject::Ptr& object, const String& attr, const Value& value)
static void PersistModAttrHelper(std::ofstream& fp, ConfigObject::Ptr& previousObject, const ConfigObject::Ptr& object, const String& attr, const Value& value)
{
if (object != previousObject) {
if (previousObject)
cw->EmitRaw("}\n\n");
if (previousObject) {
ConfigWriter::EmitRaw(fp, "\tobj.version = ");
ConfigWriter::EmitValue(fp, 0, previousObject->GetVersion());
ConfigWriter::EmitRaw(fp, "\n}\n\n");
}
cw->EmitRaw("var obj = ");
ConfigWriter::EmitRaw(fp, "var obj = ");
Array::Ptr args1 = new Array();
args1->Add(object->GetReflectionType()->GetName());
args1->Add(object->GetName());
cw->EmitFunctionCall("get_object", args1);
ConfigWriter::EmitFunctionCall(fp, "get_object", args1);
cw->EmitRaw("\nif (obj) {\n");
ConfigWriter::EmitRaw(fp, "\nif (obj) {\n");
}
cw->EmitRaw("\tobj.");
ConfigWriter::EmitRaw(fp, "\tobj.");
Array::Ptr args2 = new Array();
args2->Add(attr);
args2->Add(value);
cw->EmitFunctionCall("modify_attribute", args2);
ConfigWriter::EmitFunctionCall(fp, "modify_attribute", args2);
cw->EmitRaw("\n");
ConfigWriter::EmitRaw(fp, "\n");
previousObject = object;
}
@ -171,12 +174,16 @@ void IcingaApplication::DumpProgramState(void)
{
ConfigObject::DumpObjects(GetStatePath());
ConfigWriter::Ptr cw = new ConfigWriter(GetModAttrPath());
String path = GetModAttrPath();
std::ofstream fp(path.CStr(), std::ofstream::out | std::ostream::trunc);
ConfigObject::Ptr previousObject;
ConfigObject::DumpModifiedAttributes(boost::bind(&PersistModAttrHelper, cw, boost::ref(previousObject), _1, _2, _3));
ConfigObject::DumpModifiedAttributes(boost::bind(&PersistModAttrHelper, boost::ref(fp), boost::ref(previousObject), _1, _2, _3));
if (previousObject)
cw->EmitRaw("\n}\n");
if (previousObject) {
ConfigWriter::EmitRaw(fp, "\tobj.version = ");
ConfigWriter::EmitValue(fp, 0, previousObject->GetVersion());
ConfigWriter::EmitRaw(fp, "\n}\n");
}
}
IcingaApplication::Ptr IcingaApplication::GetInstance(void)

View File

@ -217,12 +217,12 @@ Value StatusTable::NumServicesAccessor(const Value&)
Value StatusTable::ProgramVersionAccessor(const Value&)
{
return Application::GetVersion();
return Application::GetAppVersion();
}
Value StatusTable::LivestatusVersionAccessor(const Value&)
{
return Application::GetVersion();
return Application::GetAppVersion();
}
Value StatusTable::LivestatusActiveConnectionsAccessor(const Value&)

View File

@ -97,7 +97,7 @@ void IcingaCheckTask::ScriptFunc(const Checkable::Ptr& service, const CheckResul
perfdata->Add(new PerfdataValue("num_hosts_acknowledged", hs.hosts_acknowledged));
cr->SetOutput("Icinga 2 has been running for " + Utility::FormatDuration(uptime) +
". Version: " + Application::GetVersion());
". Version: " + Application::GetAppVersion());
cr->SetPerformanceData(perfdata);
cr->SetState(ServiceOK);

View File

@ -22,8 +22,8 @@ mkclass_target(zone.ti zone.tcpp zone.thpp)
set(remote_SOURCES
actionshandler.cpp apiaction.cpp
apifunction.cpp apilistener.cpp apilistener.thpp apilistener-sync.cpp
apiuser.cpp apiuser.thpp authority.cpp base64.cpp
apifunction.cpp apilistener.cpp apilistener.thpp apilistener-configsync.cpp
apilistener-filesync.cpp apiuser.cpp apiuser.thpp authority.cpp base64.cpp
configfileshandler.cpp configpackageshandler.cpp configpackageutility.cpp configobjectutility.cpp
configstageshandler.cpp createobjecthandler.cpp deleteobjecthandler.cpp
endpoint.cpp endpoint.thpp filterutility.cpp

View File

@ -0,0 +1,143 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2015 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 "remote/apilistener.hpp"
#include "remote/apifunction.hpp"
#include "remote/configobjectutility.hpp"
#include "remote/jsonrpc.hpp"
#include "base/configtype.hpp"
#include "base/json.hpp"
#include <fstream>
using namespace icinga;
INITIALIZE_ONCE(&ApiListener::StaticInitialize);
REGISTER_APIFUNCTION(UpdateObject, config, &ApiListener::ConfigUpdateObjectAPIHandler);
REGISTER_APIFUNCTION(DeleteObject, config, &ApiListener::ConfigDeleteObjectAPIHandler);
void ApiListener::StaticInitialize(void)
{
ConfigObject::OnActiveChanged.connect(&ApiListener::ConfigUpdateObjectHandler);
ConfigObject::OnVersionChanged.connect(&ApiListener::ConfigUpdateObjectHandler);
}
void ApiListener::ConfigUpdateObjectHandler(const ConfigObject::Ptr& object, const Value& cookie)
{
ApiListener::Ptr listener = ApiListener::GetInstance();
if (!listener)
return;
if (object->IsActive()) {
/* Sync object config */
listener->UpdateConfigObject(object, cookie);
} else {
/* Delete object */
}
}
Value ApiListener::ConfigUpdateObjectAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
{
Log(LogWarning, "ApiListener")
<< "Received update for object: " << JsonEncode(params);
ConfigType::Ptr dtype = ConfigType::GetByName(params->Get("type"));
if (!dtype) {
Log(LogCritical, "ApiListener")
<< "Config type '" << params->Get("type") << "' does not exist.";
return Empty;
}
ConfigObject::Ptr object = dtype->GetObject(params->Get("name"));
if (!object) {
Array::Ptr errors = new Array();
if (!ConfigObjectUtility::CreateObject(Type::GetByName(params->Get("type")),
params->Get("name"), params->Get("config"), errors)) {
Log(LogCritical, "ApiListener", "Could not create object:");
ObjectLock olock(errors);
BOOST_FOREACH(const String& error, errors) {
Log(LogCritical, "ApiListener", error);
}
}
//TODO-MA: modified attributes, same version
}
return Empty;
}
Value ApiListener::ConfigDeleteObjectAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
{
return Empty;
}
void ApiListener::UpdateConfigObject(const ConfigObject::Ptr& object, const MessageOrigin::Ptr& origin,
const JsonRpcConnection::Ptr& client)
{
if (object->GetPackage() != "_api")
return;
Dictionary::Ptr message = new Dictionary();
message->Set("jsonrpc", "2.0");
message->Set("method", "config::UpdateObject");
Dictionary::Ptr params = new Dictionary();
params->Set("name", object->GetName());
params->Set("type", object->GetType()->GetName());
params->Set("version", object->GetVersion());
String file = ConfigObjectUtility::GetObjectConfigPath(object->GetReflectionType(), object->GetName());
std::ifstream fp(file.CStr(), std::ifstream::binary);
if (!fp)
return;
String content((std::istreambuf_iterator<char>(fp)), std::istreambuf_iterator<char>());
params->Set("config", content);
Dictionary::Ptr original_attributes = object->GetOriginalAttributes();
Dictionary::Ptr modified_attributes = new Dictionary();
if (original_attributes) {
ObjectLock olock(original_attributes);
BOOST_FOREACH(const Dictionary::Pair& kv, original_attributes) {
int fid = object->GetReflectionType()->GetFieldId(kv.first);
//TODO-MA: vars.os
Value value = static_cast<Object::Ptr>(object)->GetField(fid);
modified_attributes->Set(kv.first, value);
}
}
params->Set("modified_attributes", modified_attributes);
message->Set("params", params);
Log(LogWarning, "ApiListener")
<< "Sent update for object: " << JsonEncode(params);
if (client)
JsonRpc::SendMessage(client->GetStream(), message);
else
RelayMessage(origin, object, message, false);
}

View File

@ -46,6 +46,8 @@ public:
DECLARE_OBJECT(ApiListener);
DECLARE_OBJECTNAME(ApiListener);
static void StaticInitialize(void);
static boost::signals2::signal<void(bool)> OnMasterChanged;
ApiListener(void);
@ -73,8 +75,14 @@ public:
void RemoveHttpClient(const HttpServerConnection::Ptr& aclient);
std::set<HttpServerConnection::Ptr> GetHttpClients(void) const;
/* filesync */
static Value ConfigUpdateHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);
/* configsync */
static void ConfigUpdateObjectHandler(const ConfigObject::Ptr& object, const Value& cookie);
static Value ConfigUpdateObjectAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);
static Value ConfigDeleteObjectAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);
static Value HelloAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);
protected:
virtual void OnConfigLoaded(void) override;
@ -121,6 +129,9 @@ private:
static bool IsConfigMaster(const Zone::Ptr& zone);
static void ConfigGlobHandler(Dictionary::Ptr& config, const String& path, const String& file);
void SendConfigUpdate(const JsonRpcConnection::Ptr& aclient);
void UpdateConfigObject(const ConfigObject::Ptr& object, const MessageOrigin::Ptr& origin,
const JsonRpcConnection::Ptr& client = JsonRpcConnection::Ptr());
};
}

View File

@ -19,7 +19,7 @@
#include "remote/configobjectutility.hpp"
#include "remote/configpackageutility.hpp"
#include "config/configitembuilder.hpp"
#include "config/configcompiler.hpp"
#include "config/configitem.hpp"
#include "config/configwriter.hpp"
#include "base/exception.hpp"
@ -37,13 +37,22 @@ String ConfigObjectUtility::GetConfigDir(void)
ConfigPackageUtility::GetActiveStage("_api");
}
String ConfigObjectUtility::GetObjectConfigPath(const Type::Ptr& type, const String& fullName)
{
String typeDir = type->GetPluralName();
boost::algorithm::to_lower(typeDir);
return GetConfigDir() + "/conf.d/" + typeDir +
"/" + EscapeName(fullName) + ".conf";
}
String ConfigObjectUtility::EscapeName(const String& name)
{
return Utility::EscapeString(name, "<>:\"/\\|?*", true);
}
bool ConfigObjectUtility::CreateObject(const Type::Ptr& type, const String& fullName,
const Array::Ptr& templates, const Dictionary::Ptr& attrs, const Array::Ptr& errors)
String ConfigObjectUtility::CreateObjectConfig(const Type::Ptr& type, const String& fullName,
const Array::Ptr& templates, const Dictionary::Ptr& attrs)
{
NameComposer *nc = dynamic_cast<NameComposer *>(type.get());
Dictionary::Ptr nameParts;
@ -55,48 +64,47 @@ bool ConfigObjectUtility::CreateObject(const Type::Ptr& type, const String& full
} else
name = fullName;
ConfigItemBuilder::Ptr builder = new ConfigItemBuilder();
builder->SetType(type->GetName());
builder->SetName(name);
builder->SetScope(ScriptGlobal::GetGlobals());
builder->SetPackage("_api");
Dictionary::Ptr allAttrs = new Dictionary();
if (templates) {
ObjectLock olock(templates);
BOOST_FOREACH(const String& tmpl, templates) {
ImportExpression *expr = new ImportExpression(MakeLiteral(tmpl));
builder->AddExpression(expr);
}
if (attrs)
attrs->CopyTo(allAttrs);
if (nameParts)
nameParts->CopyTo(allAttrs);
allAttrs->Remove("name");
std::ostringstream config;
ConfigWriter::EmitConfigItem(config, type->GetName(), name, false, templates, allAttrs);
ConfigWriter::EmitRaw(config, "\n");
return config.str();
}
bool ConfigObjectUtility::CreateObject(const Type::Ptr& type, const String& fullName,
const String& config, const Array::Ptr& errors)
{
if (!ConfigPackageUtility::PackageExists("_api")) {
ConfigPackageUtility::CreatePackage("_api");
String stage = ConfigPackageUtility::CreateStage("_api");
ConfigPackageUtility::ActivateStage("_api", stage);
}
if (nameParts) {
ObjectLock olock(nameParts);
BOOST_FOREACH(const Dictionary::Pair& kv, nameParts) {
SetExpression *expr = new SetExpression(MakeIndexer(ScopeThis, kv.first), OpSetLiteral, MakeLiteral(kv.second));
builder->AddExpression(expr);
}
}
String path = GetObjectConfigPath(type, fullName);
Utility::MkDirP(Utility::DirName(path), 0700);
std::ofstream fp(path.CStr(), std::ofstream::out | std::ostream::trunc);
fp << config;
fp.close();
Expression *expr = ConfigCompiler::CompileFile(path, String(), "_api");
if (attrs) {
ObjectLock olock(attrs);
BOOST_FOREACH(const Dictionary::Pair& kv, attrs) {
std::vector<String> tokens;
boost::algorithm::split(tokens, kv.first, boost::is_any_of("."));
Expression *expr = new GetScopeExpression(ScopeThis);
BOOST_FOREACH(const String& val, tokens) {
expr = new IndexerExpression(expr, MakeLiteral(val));
}
SetExpression *aexpr = new SetExpression(expr, OpSetLiteral, MakeLiteral(kv.second));
builder->AddExpression(aexpr);
}
}
try {
ConfigItem::Ptr item = builder->Compile();
item->Register();
ScriptFrame frame;
expr->Evaluate(frame);
delete expr;
expr = NULL;
WorkQueue upq;
@ -106,43 +114,18 @@ bool ConfigObjectUtility::CreateObject(const Type::Ptr& type, const String& full
errors->Add(DiagnosticInformation(ex));
}
}
return false;
}
} catch (const std::exception& ex) {
delete expr;
if (errors)
errors->Add(DiagnosticInformation(ex));
return false;
}
if (!ConfigPackageUtility::PackageExists("_api")) {
ConfigPackageUtility::CreatePackage("_api");
String stage = ConfigPackageUtility::CreateStage("_api");
ConfigPackageUtility::ActivateStage("_api", stage);
}
String typeDir = type->GetPluralName();
boost::algorithm::to_lower(typeDir);
String path = GetConfigDir() + "/conf.d/" + typeDir;
Utility::MkDirP(path, 0700);
path += "/" + EscapeName(fullName) + ".conf";
Dictionary::Ptr allAttrs = new Dictionary();
attrs->CopyTo(allAttrs);
if (nameParts)
nameParts->CopyTo(allAttrs);
allAttrs->Remove("name");
ConfigWriter::Ptr cw = new ConfigWriter(path);
cw->EmitConfigItem(type->GetName(), name, false, templates, allAttrs);
cw->EmitRaw("\n");
return true;
}
@ -168,7 +151,7 @@ bool ConfigObjectUtility::DeleteObjectHelper(const ConfigObject::Ptr& object, bo
Type::Ptr type = object->GetReflectionType();
ConfigItem::Ptr item = ConfigItem::GetObject(type->GetName(), object->GetName());
ConfigItem::Ptr item = ConfigItem::GetByTypeAndName(type->GetName(), object->GetName());
try {
object->Deactivate();
@ -181,16 +164,12 @@ bool ConfigObjectUtility::DeleteObjectHelper(const ConfigObject::Ptr& object, bo
} catch (const std::exception& ex) {
if (errors)
errors->Add(DiagnosticInformation(ex));
return false;
}
String typeDir = type->GetPluralName();
boost::algorithm::to_lower(typeDir);
String path = GetConfigDir() + "/conf.d/" + typeDir +
"/" + EscapeName(object->GetName()) + ".conf";
String path = GetObjectConfigPath(object->GetReflectionType(), object->GetName());
if (Utility::PathExists(path)) {
if (unlink(path.CStr()) < 0) {
BOOST_THROW_EXCEPTION(posix_error()

View File

@ -39,10 +39,13 @@ class I2_REMOTE_API ConfigObjectUtility
public:
static String GetConfigDir(void);
static String GetObjectConfigPath(const Type::Ptr& type, const String& fullName);
static String CreateObjectConfig(const Type::Ptr& type, const String& fullName,
const Array::Ptr& templates, const Dictionary::Ptr& attrs);
static bool CreateObject(const Type::Ptr& type, const String& fullName,
const Array::Ptr& templates, const Dictionary::Ptr& attrs,
const Array::Ptr& errors);
const String& config, const Array::Ptr& errors);
static bool DeleteObject(const ConfigObject::Ptr& object, bool cascade, const Array::Ptr& errors);

View File

@ -52,7 +52,9 @@ bool CreateObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r
String status;
Array::Ptr errors = new Array();
if (!ConfigObjectUtility::CreateObject(type, name, templates, attrs, errors)) {
String config = ConfigObjectUtility::CreateObjectConfig(type, name, templates, attrs);
if (!ConfigObjectUtility::CreateObject(type, name, config, errors)) {
result1->Set("errors", errors);
code = 500;
status = "Object could not be created.";

View File

@ -171,7 +171,7 @@ void HttpRequest::FinishHeaders(void)
}
if (m_State == HttpRequestHeaders) {
AddHeader("User-Agent", "Icinga/" + Application::GetVersion());
AddHeader("User-Agent", "Icinga/" + Application::GetAppVersion());
if (ProtocolVersion == HttpVersion11)
AddHeader("Transfer-Encoding", "chunked");

View File

@ -65,7 +65,7 @@ void HttpResponse::FinishHeaders(void)
if (m_Request.ProtocolVersion == HttpVersion11)
AddHeader("Transfer-Encoding", "chunked");
AddHeader("Server", "Icinga/" + Application::GetVersion());
AddHeader("Server", "Icinga/" + Application::GetAppVersion());
m_Stream->Write("\r\n", 2);
m_State = HttpResponseBody;
}

View File

@ -104,8 +104,6 @@ void JsonRpcConnection::SendMessageSync(const Dictionary::Ptr& message)
std::ostringstream info;
info << "Error while sending JSON-RPC message for identity '" << m_Identity << "'";
Log(LogWarning, "JsonRpcConnection")
<< info.str();
Log(LogDebug, "JsonRpcConnection")
<< info.str() << "\n" << DiagnosticInformation(ex);
Disconnect();
@ -181,8 +179,6 @@ bool JsonRpcConnection::ProcessMessage(void)
std::ostringstream info;
info << "Error while processing message for identity '" << m_Identity << "'";
Log(LogWarning, "JsonRpcConnection")
<< info.str();
Log(LogDebug, "JsonRpcConnection")
<< info.str() << "\n" << DiagnosticInformation(ex);
}