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

View File

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

View File

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

View File

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

View File

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

View File

@ -94,6 +94,9 @@ abstract class ConfigObject : ConfigObjectBase
[protected] bool state_loaded; [protected] bool state_loaded;
Dictionary::Ptr original_attributes; 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; scriptFrame.Sandboxed = true;
} }
std::cout << "Icinga (version: " << Application::GetVersion() << ")\n"; std::cout << "Icinga (version: " << Application::GetAppVersion() << ")\n";
while (std::cin.good()) { while (std::cin.good()) {
String fileName = "<" + Convert::ToString(next_line) + ">"; 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); Logger::DisableTimestamp(false);
Log(LogInformation, "cli") Log(LogInformation, "cli")
<< "Icinga application loader (version: " << Application::GetVersion() << "Icinga application loader (version: " << Application::GetAppVersion()
#ifdef I2_DEBUG #ifdef I2_DEBUG
<< "; debug" << "; debug"
#endif /* I2_DEBUG */ #endif /* I2_DEBUG */

View File

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

View File

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

View File

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

View File

@ -61,7 +61,9 @@ public:
DebugInfo GetDebugInfo(void) const; DebugInfo GetDebugInfo(void) const;
Dictionary::Ptr GetScope(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); const String& name);
static bool CommitItems(WorkQueue& upq); static bool CommitItems(WorkQueue& upq);

View File

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

View File

@ -34,34 +34,30 @@ namespace icinga
* *
* @ingroup config * @ingroup config
*/ */
class I2_CONFIG_API ConfigWriter : public Object class I2_CONFIG_API ConfigWriter
{ {
public: 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); 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,
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,
const Array::Ptr& imports, const Dictionary::Ptr& attrs); const Array::Ptr& imports, const Dictionary::Ptr& attrs);
void EmitComment(const String& text); static void EmitComment(std::ostream& fp, const String& text);
void EmitFunctionCall(const String& name, const Array::Ptr& arguments); static void EmitFunctionCall(std::ostream& fp, const String& name, const Array::Ptr& arguments);
private: private:
std::ofstream m_FP; ConfigWriter(void);
static String EscapeIcingaString(const String& str); static String EscapeIcingaString(const String& str);
}; };

View File

@ -724,7 +724,7 @@ ExpressionResult ImportExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhi
if (!name.IsString()) if (!name.IsString())
BOOST_THROW_EXCEPTION(ScriptError("Template/object name must be a string", m_DebugInfo)); 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) if (!item)
BOOST_THROW_EXCEPTION(ScriptError("Import references unknown template: '" + name + "'", m_DebugInfo)); BOOST_THROW_EXCEPTION(ScriptError("Import references unknown template: '" + name + "'", m_DebugInfo));

View File

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

View File

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

View File

@ -331,7 +331,7 @@ void IdoMysqlConnection::Reconnect(void)
/* record connection */ /* record connection */
Query("INSERT INTO " + GetTablePrefix() + "conninfo " + Query("INSERT INTO " + GetTablePrefix() + "conninfo " +
"(instance_id, connect_time, last_checkin_time, agent_name, agent_version, connect_type, data_start_time) VALUES (" "(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())"); + "', '" + (reconnect ? "RECONNECT" : "INITIAL") + "', NOW())");
/* clear config tables for the initial config dump */ /* clear config tables for the initial config dump */

View File

@ -323,7 +323,7 @@ void IdoPgsqlConnection::Reconnect(void)
/* record connection */ /* record connection */
Query("INSERT INTO " + GetTablePrefix() + "conninfo " + Query("INSERT INTO " + GetTablePrefix() + "conninfo " +
"(instance_id, connect_time, last_checkin_time, agent_name, agent_version, connect_type, data_start_time) VALUES (" "(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())"); + "', E'" + (reconnect ? "RECONNECT" : "INITIAL") + "', NOW())");
/* clear config tables for the initial config dump */ /* 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("enable_perfdata", icingaapplication->GetEnablePerfdata());
stats->Set("pid", Utility::GetPid()); stats->Set("pid", Utility::GetPid());
stats->Set("program_start", Application::GetStartTime()); stats->Set("program_start", Application::GetStartTime());
stats->Set("version", Application::GetVersion()); stats->Set("version", Application::GetAppVersion());
nodes->Set(icingaapplication->GetName(), stats); nodes->Set(icingaapplication->GetName(), stats);
} }
@ -139,30 +139,33 @@ void IcingaApplication::OnShutdown(void)
DumpProgramState(); 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 (object != previousObject) {
if (previousObject) if (previousObject) {
cw->EmitRaw("}\n\n"); 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(); Array::Ptr args1 = new Array();
args1->Add(object->GetReflectionType()->GetName()); args1->Add(object->GetReflectionType()->GetName());
args1->Add(object->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(); Array::Ptr args2 = new Array();
args2->Add(attr); args2->Add(attr);
args2->Add(value); args2->Add(value);
cw->EmitFunctionCall("modify_attribute", args2); ConfigWriter::EmitFunctionCall(fp, "modify_attribute", args2);
cw->EmitRaw("\n"); ConfigWriter::EmitRaw(fp, "\n");
previousObject = object; previousObject = object;
} }
@ -171,12 +174,16 @@ void IcingaApplication::DumpProgramState(void)
{ {
ConfigObject::DumpObjects(GetStatePath()); 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::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) if (previousObject) {
cw->EmitRaw("\n}\n"); ConfigWriter::EmitRaw(fp, "\tobj.version = ");
ConfigWriter::EmitValue(fp, 0, previousObject->GetVersion());
ConfigWriter::EmitRaw(fp, "\n}\n");
}
} }
IcingaApplication::Ptr IcingaApplication::GetInstance(void) IcingaApplication::Ptr IcingaApplication::GetInstance(void)

View File

@ -217,12 +217,12 @@ Value StatusTable::NumServicesAccessor(const Value&)
Value StatusTable::ProgramVersionAccessor(const Value&) Value StatusTable::ProgramVersionAccessor(const Value&)
{ {
return Application::GetVersion(); return Application::GetAppVersion();
} }
Value StatusTable::LivestatusVersionAccessor(const Value&) Value StatusTable::LivestatusVersionAccessor(const Value&)
{ {
return Application::GetVersion(); return Application::GetAppVersion();
} }
Value StatusTable::LivestatusActiveConnectionsAccessor(const Value&) 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)); perfdata->Add(new PerfdataValue("num_hosts_acknowledged", hs.hosts_acknowledged));
cr->SetOutput("Icinga 2 has been running for " + Utility::FormatDuration(uptime) + cr->SetOutput("Icinga 2 has been running for " + Utility::FormatDuration(uptime) +
". Version: " + Application::GetVersion()); ". Version: " + Application::GetAppVersion());
cr->SetPerformanceData(perfdata); cr->SetPerformanceData(perfdata);
cr->SetState(ServiceOK); cr->SetState(ServiceOK);

View File

@ -22,8 +22,8 @@ mkclass_target(zone.ti zone.tcpp zone.thpp)
set(remote_SOURCES set(remote_SOURCES
actionshandler.cpp apiaction.cpp actionshandler.cpp apiaction.cpp
apifunction.cpp apilistener.cpp apilistener.thpp apilistener-sync.cpp apifunction.cpp apilistener.cpp apilistener.thpp apilistener-configsync.cpp
apiuser.cpp apiuser.thpp authority.cpp base64.cpp apilistener-filesync.cpp apiuser.cpp apiuser.thpp authority.cpp base64.cpp
configfileshandler.cpp configpackageshandler.cpp configpackageutility.cpp configobjectutility.cpp configfileshandler.cpp configpackageshandler.cpp configpackageutility.cpp configobjectutility.cpp
configstageshandler.cpp createobjecthandler.cpp deleteobjecthandler.cpp configstageshandler.cpp createobjecthandler.cpp deleteobjecthandler.cpp
endpoint.cpp endpoint.thpp filterutility.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_OBJECT(ApiListener);
DECLARE_OBJECTNAME(ApiListener); DECLARE_OBJECTNAME(ApiListener);
static void StaticInitialize(void);
static boost::signals2::signal<void(bool)> OnMasterChanged; static boost::signals2::signal<void(bool)> OnMasterChanged;
ApiListener(void); ApiListener(void);
@ -73,8 +75,14 @@ public:
void RemoveHttpClient(const HttpServerConnection::Ptr& aclient); void RemoveHttpClient(const HttpServerConnection::Ptr& aclient);
std::set<HttpServerConnection::Ptr> GetHttpClients(void) const; std::set<HttpServerConnection::Ptr> GetHttpClients(void) const;
/* filesync */
static Value ConfigUpdateHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); 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); static Value HelloAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);
protected: protected:
virtual void OnConfigLoaded(void) override; virtual void OnConfigLoaded(void) override;
@ -121,6 +129,9 @@ private:
static bool IsConfigMaster(const Zone::Ptr& zone); static bool IsConfigMaster(const Zone::Ptr& zone);
static void ConfigGlobHandler(Dictionary::Ptr& config, const String& path, const String& file); static void ConfigGlobHandler(Dictionary::Ptr& config, const String& path, const String& file);
void SendConfigUpdate(const JsonRpcConnection::Ptr& aclient); 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/configobjectutility.hpp"
#include "remote/configpackageutility.hpp" #include "remote/configpackageutility.hpp"
#include "config/configitembuilder.hpp" #include "config/configcompiler.hpp"
#include "config/configitem.hpp" #include "config/configitem.hpp"
#include "config/configwriter.hpp" #include "config/configwriter.hpp"
#include "base/exception.hpp" #include "base/exception.hpp"
@ -37,13 +37,22 @@ String ConfigObjectUtility::GetConfigDir(void)
ConfigPackageUtility::GetActiveStage("_api"); 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) String ConfigObjectUtility::EscapeName(const String& name)
{ {
return Utility::EscapeString(name, "<>:\"/\\|?*", true); return Utility::EscapeString(name, "<>:\"/\\|?*", true);
} }
bool ConfigObjectUtility::CreateObject(const Type::Ptr& type, const String& fullName, String ConfigObjectUtility::CreateObjectConfig(const Type::Ptr& type, const String& fullName,
const Array::Ptr& templates, const Dictionary::Ptr& attrs, const Array::Ptr& errors) const Array::Ptr& templates, const Dictionary::Ptr& attrs)
{ {
NameComposer *nc = dynamic_cast<NameComposer *>(type.get()); NameComposer *nc = dynamic_cast<NameComposer *>(type.get());
Dictionary::Ptr nameParts; Dictionary::Ptr nameParts;
@ -55,48 +64,47 @@ bool ConfigObjectUtility::CreateObject(const Type::Ptr& type, const String& full
} else } else
name = fullName; name = fullName;
ConfigItemBuilder::Ptr builder = new ConfigItemBuilder(); Dictionary::Ptr allAttrs = new Dictionary();
builder->SetType(type->GetName());
builder->SetName(name);
builder->SetScope(ScriptGlobal::GetGlobals());
builder->SetPackage("_api");
if (templates) { if (attrs)
ObjectLock olock(templates); attrs->CopyTo(allAttrs);
BOOST_FOREACH(const String& tmpl, templates) {
ImportExpression *expr = new ImportExpression(MakeLiteral(tmpl)); if (nameParts)
builder->AddExpression(expr); 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) { String path = GetObjectConfigPath(type, fullName);
ObjectLock olock(nameParts); Utility::MkDirP(Utility::DirName(path), 0700);
BOOST_FOREACH(const Dictionary::Pair& kv, nameParts) {
SetExpression *expr = new SetExpression(MakeIndexer(ScopeThis, kv.first), OpSetLiteral, MakeLiteral(kv.second));
builder->AddExpression(expr);
}
}
if (attrs) { std::ofstream fp(path.CStr(), std::ofstream::out | std::ostream::trunc);
ObjectLock olock(attrs); fp << config;
BOOST_FOREACH(const Dictionary::Pair& kv, attrs) { fp.close();
std::vector<String> tokens;
boost::algorithm::split(tokens, kv.first, boost::is_any_of("."));
Expression *expr = new GetScopeExpression(ScopeThis); Expression *expr = ConfigCompiler::CompileFile(path, String(), "_api");
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 { try {
ConfigItem::Ptr item = builder->Compile(); ScriptFrame frame;
item->Register(); expr->Evaluate(frame);
delete expr;
expr = NULL;
WorkQueue upq; WorkQueue upq;
@ -110,39 +118,14 @@ bool ConfigObjectUtility::CreateObject(const Type::Ptr& type, const String& full
return false; return false;
} }
} catch (const std::exception& ex) { } catch (const std::exception& ex) {
delete expr;
if (errors) if (errors)
errors->Add(DiagnosticInformation(ex)); errors->Add(DiagnosticInformation(ex));
return false; 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; return true;
} }
@ -168,7 +151,7 @@ bool ConfigObjectUtility::DeleteObjectHelper(const ConfigObject::Ptr& object, bo
Type::Ptr type = object->GetReflectionType(); Type::Ptr type = object->GetReflectionType();
ConfigItem::Ptr item = ConfigItem::GetObject(type->GetName(), object->GetName()); ConfigItem::Ptr item = ConfigItem::GetByTypeAndName(type->GetName(), object->GetName());
try { try {
object->Deactivate(); object->Deactivate();
@ -185,11 +168,7 @@ bool ConfigObjectUtility::DeleteObjectHelper(const ConfigObject::Ptr& object, bo
return false; return false;
} }
String typeDir = type->GetPluralName(); String path = GetObjectConfigPath(object->GetReflectionType(), object->GetName());
boost::algorithm::to_lower(typeDir);
String path = GetConfigDir() + "/conf.d/" + typeDir +
"/" + EscapeName(object->GetName()) + ".conf";
if (Utility::PathExists(path)) { if (Utility::PathExists(path)) {
if (unlink(path.CStr()) < 0) { if (unlink(path.CStr()) < 0) {

View File

@ -39,10 +39,13 @@ class I2_REMOTE_API ConfigObjectUtility
public: public:
static String GetConfigDir(void); 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, static bool CreateObject(const Type::Ptr& type, const String& fullName,
const Array::Ptr& templates, const Dictionary::Ptr& attrs, const String& config, const Array::Ptr& errors);
const Array::Ptr& errors);
static bool DeleteObject(const ConfigObject::Ptr& object, bool cascade, 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; String status;
Array::Ptr errors = new Array(); 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); result1->Set("errors", errors);
code = 500; code = 500;
status = "Object could not be created."; status = "Object could not be created.";

View File

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

View File

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

View File

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