From: Gunnar Beutner Date: Thu, 20 Aug 2015 14:43:03 +0000 (+0200) Subject: Implement config object sync X-Git-Tag: v2.4.0~320 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=6fa58a520c5595296c7de7972ddf6fdfbfcdc56b;p=icinga2 Implement config object sync Adds object version. refs #9927 --- diff --git a/icinga-app/icinga.cpp b/icinga-app/icinga.cpp index 12601381e..454257709 100644 --- a/icinga-app/icinga.cpp +++ b/icinga-app/icinga.cpp @@ -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(); diff --git a/lib/base/application-version.cpp b/lib/base/application-version.cpp index c13166e7f..6bc51004d 100644 --- a/lib/base/application-version.cpp +++ b/lib/base/application-version.cpp @@ -22,7 +22,7 @@ using namespace icinga; -String Application::GetVersion(void) +String Application::GetAppVersion(void) { return VERSION; } diff --git a/lib/base/application.cpp b/lib/base/application.cpp index ec62ad083..afa2e66a3 100644 --- a/lib/base/application.cpp +++ b/lib/base/application.cpp @@ -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" diff --git a/lib/base/application.hpp b/lib/base/application.hpp index fe6472dbc..eeb8f9c3b 100644 --- a/lib/base/application.hpp +++ b/lib/base/application.hpp @@ -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); diff --git a/lib/base/configobject.cpp b/lib/base/configobject.cpp index 1d895676c..871336068 100644 --- a/lib/base/configobject.cpp +++ b/lib/base/configobject.cpp @@ -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(); diff --git a/lib/base/configobject.ti b/lib/base/configobject.ti index b7f60cb0d..1e4a3f83f 100644 --- a/lib/base/configobject.ti +++ b/lib/base/configobject.ti @@ -94,6 +94,9 @@ abstract class ConfigObject : ConfigObjectBase [protected] bool state_loaded; Dictionary::Ptr original_attributes; + [state] int version { + default {{{ return 1; }}} + }; }; } diff --git a/lib/cli/consolecommand.cpp b/lib/cli/consolecommand.cpp index 7ba81664a..cea3f884d 100644 --- a/lib/cli/consolecommand.cpp +++ b/lib/cli/consolecommand.cpp @@ -186,7 +186,7 @@ int ConsoleCommand::Run(const po::variables_map& vm, const std::vector"; diff --git a/lib/cli/daemoncommand.cpp b/lib/cli/daemoncommand.cpp index 7c9b6a625..075b91474 100644 --- a/lib/cli/daemoncommand.cpp +++ b/lib/cli/daemoncommand.cpp @@ -209,7 +209,7 @@ int DaemonCommand::Run(const po::variables_map& vm, const std::vectorGetLength() > 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 tokens; - boost::algorithm::split(tokens, kv.first, boost::is_any_of(".")); - - EmitIdentifier(tokens[0], true); - - for (std::vector::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 tokens; + boost::algorithm::split(tokens, kv.first, boost::is_any_of(".")); + + EmitIdentifier(fp, tokens[0], true); + + for (std::vector::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()) - EmitArray(val); + EmitArray(fp, val); else if (val.IsObjectType()) - 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 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) diff --git a/lib/config/configwriter.hpp b/lib/config/configwriter.hpp index 325ee53cd..c7fa1bb8e 100644 --- a/lib/config/configwriter.hpp +++ b/lib/config/configwriter.hpp @@ -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); }; diff --git a/lib/config/expression.cpp b/lib/config/expression.cpp index fcee20777..773a871a7 100644 --- a/lib/config/expression.cpp +++ b/lib/config/expression.cpp @@ -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)); diff --git a/lib/config/vmops.hpp b/lib/config/vmops.hpp index f342ac0c4..e4cde9f26 100644 --- a/lib/config/vmops.hpp +++ b/lib/config/vmops.hpp @@ -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; diff --git a/lib/db_ido/dbconnection.cpp b/lib/db_ido/dbconnection.cpp index 986995b18..eca07b802 100644 --- a/lib/db_ido/dbconnection.cpp +++ b/lib/db_ido/dbconnection.cpp @@ -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); diff --git a/lib/db_ido_mysql/idomysqlconnection.cpp b/lib/db_ido_mysql/idomysqlconnection.cpp index 9071f81e5..7e9c2c1b8 100644 --- a/lib/db_ido_mysql/idomysqlconnection.cpp +++ b/lib/db_ido_mysql/idomysqlconnection.cpp @@ -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(m_InstanceID)) + ", NOW(), NOW(), 'icinga2 db_ido_mysql', '" + Escape(Application::GetVersion()) + + Convert::ToString(static_cast(m_InstanceID)) + ", NOW(), NOW(), 'icinga2 db_ido_mysql', '" + Escape(Application::GetAppVersion()) + "', '" + (reconnect ? "RECONNECT" : "INITIAL") + "', NOW())"); /* clear config tables for the initial config dump */ diff --git a/lib/db_ido_pgsql/idopgsqlconnection.cpp b/lib/db_ido_pgsql/idopgsqlconnection.cpp index 8b28930c9..c9e171bf2 100644 --- a/lib/db_ido_pgsql/idopgsqlconnection.cpp +++ b/lib/db_ido_pgsql/idopgsqlconnection.cpp @@ -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(m_InstanceID)) + ", NOW(), NOW(), E'icinga2 db_ido_pgsql', E'" + Escape(Application::GetVersion()) + + Convert::ToString(static_cast(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 */ diff --git a/lib/icinga/icingaapplication.cpp b/lib/icinga/icingaapplication.cpp index 77d330dcf..bba902fbb 100644 --- a/lib/icinga/icingaapplication.cpp +++ b/lib/icinga/icingaapplication.cpp @@ -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) diff --git a/lib/livestatus/statustable.cpp b/lib/livestatus/statustable.cpp index 1bcd6c305..fea0c82da 100644 --- a/lib/livestatus/statustable.cpp +++ b/lib/livestatus/statustable.cpp @@ -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&) diff --git a/lib/methods/icingachecktask.cpp b/lib/methods/icingachecktask.cpp index 5e2643db9..fef332c0c 100644 --- a/lib/methods/icingachecktask.cpp +++ b/lib/methods/icingachecktask.cpp @@ -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); diff --git a/lib/remote/CMakeLists.txt b/lib/remote/CMakeLists.txt index f52c39b7e..8e90966a4 100644 --- a/lib/remote/CMakeLists.txt +++ b/lib/remote/CMakeLists.txt @@ -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 diff --git a/lib/remote/apilistener-configsync.cpp b/lib/remote/apilistener-configsync.cpp new file mode 100644 index 000000000..c3c44fd77 --- /dev/null +++ b/lib/remote/apilistener-configsync.cpp @@ -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 + +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(fp)), std::istreambuf_iterator()); + 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)->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); +} diff --git a/lib/remote/apilistener-sync.cpp b/lib/remote/apilistener-filesync.cpp similarity index 100% rename from lib/remote/apilistener-sync.cpp rename to lib/remote/apilistener-filesync.cpp diff --git a/lib/remote/apilistener.hpp b/lib/remote/apilistener.hpp index 4990b4101..a9a4dadb2 100644 --- a/lib/remote/apilistener.hpp +++ b/lib/remote/apilistener.hpp @@ -46,6 +46,8 @@ public: DECLARE_OBJECT(ApiListener); DECLARE_OBJECTNAME(ApiListener); + static void StaticInitialize(void); + static boost::signals2::signal OnMasterChanged; ApiListener(void); @@ -73,8 +75,14 @@ public: void RemoveHttpClient(const HttpServerConnection::Ptr& aclient); std::set 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()); }; } diff --git a/lib/remote/configobjectutility.cpp b/lib/remote/configobjectutility.cpp index 0ce87e89e..e59868a74 100644 --- a/lib/remote/configobjectutility.cpp +++ b/lib/remote/configobjectutility.cpp @@ -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(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"); - - if (templates) { - ObjectLock olock(templates); - BOOST_FOREACH(const String& tmpl, templates) { - ImportExpression *expr = new ImportExpression(MakeLiteral(tmpl)); - builder->AddExpression(expr); - } - } + Dictionary::Ptr allAttrs = new Dictionary(); - 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); - } - } + if (attrs) + attrs->CopyTo(allAttrs); - if (attrs) { - ObjectLock olock(attrs); - BOOST_FOREACH(const Dictionary::Pair& kv, attrs) { - std::vector 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); - } + 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); } - + + 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"); + 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() diff --git a/lib/remote/configobjectutility.hpp b/lib/remote/configobjectutility.hpp index f52ec8f7d..a8ff67922 100644 --- a/lib/remote/configobjectutility.hpp +++ b/lib/remote/configobjectutility.hpp @@ -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); diff --git a/lib/remote/createobjecthandler.cpp b/lib/remote/createobjecthandler.cpp index 28fdc4385..7e22e85fb 100644 --- a/lib/remote/createobjecthandler.cpp +++ b/lib/remote/createobjecthandler.cpp @@ -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."; diff --git a/lib/remote/httprequest.cpp b/lib/remote/httprequest.cpp index 28634563f..c2c8f116f 100644 --- a/lib/remote/httprequest.cpp +++ b/lib/remote/httprequest.cpp @@ -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"); diff --git a/lib/remote/httpresponse.cpp b/lib/remote/httpresponse.cpp index fdd5d663d..f3fe6a790 100644 --- a/lib/remote/httpresponse.cpp +++ b/lib/remote/httpresponse.cpp @@ -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; } diff --git a/lib/remote/jsonrpcconnection.cpp b/lib/remote/jsonrpcconnection.cpp index 6d67e10e6..7a0be8a5d 100644 --- a/lib/remote/jsonrpcconnection.cpp +++ b/lib/remote/jsonrpcconnection.cpp @@ -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); }