Implemented state retention.

This commit is contained in:
Gunnar Beutner 2012-07-24 13:13:02 +02:00
parent 44b3de76c9
commit 709cd36e83
15 changed files with 163 additions and 29 deletions

View File

@ -23,6 +23,7 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="logger.cpp" />
<ClCompile Include="netstring.cpp" />
<ClCompile Include="object.cpp" />
<ClCompile Include="objectmap.cpp" />
<ClCompile Include="objectset.cpp" />
@ -51,14 +52,15 @@
<ClInclude Include="configobject.h" />
<ClInclude Include="dictionary.h" />
<ClInclude Include="event.h" />
<ClInclude Include="fifo.h" />
<ClInclude Include="ioqueue.h" />
<ClInclude Include="netstring.h" />
<ClInclude Include="scriptfunction.h" />
<ClInclude Include="scripttask.h" />
<ClInclude Include="logger.h" />
<ClInclude Include="objectmap.h" />
<ClInclude Include="objectset.h" />
<ClInclude Include="exception.h" />
<ClInclude Include="fifo.h" />
<ClInclude Include="i2-base.h" />
<ClInclude Include="object.h" />
<ClInclude Include="process.h" />

View File

@ -19,9 +19,6 @@
<ClCompile Include="exception.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="fifo.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="object.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
@ -88,6 +85,12 @@
<ClCompile Include="i2-base.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="fifo.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="netstring.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="application.h">
@ -111,9 +114,6 @@
<ClInclude Include="exception.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="fifo.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="i2-base.h">
<Filter>Headerdateien</Filter>
</ClInclude>
@ -183,6 +183,12 @@
<ClInclude Include="ioqueue.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="fifo.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="netstring.h">
<Filter>Headerdateien</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="Quelldateien">

View File

@ -21,12 +21,23 @@
using namespace icinga;
map<pair<string, string>, ConfigObject::Ptr> ConfigObject::m_RetainedObjects;
ConfigObject::ConfigObject(Dictionary::Ptr properties, const ConfigObject::Set::Ptr& container)
: m_Container(container ? container : GetAllObjects()),
m_Properties(properties), m_Tags(boost::make_shared<Dictionary>())
{ }
{
/* restore the object's tags */
map<pair<string, string>, ConfigObject::Ptr>::iterator it;
it = m_RetainedObjects.find(make_pair(GetType(), GetName()));
if (it != m_RetainedObjects.end()) {
ConfigObject::Ptr retainedObject = it->second;
m_Tags = retainedObject->GetTags();
m_RetainedObjects.erase(it);
}
}
void ConfigObject::SetProperties(Dictionary::Ptr properties)
void ConfigObject::SetProperties(const Dictionary::Ptr& properties)
{
m_Properties = properties;
}
@ -36,6 +47,11 @@ Dictionary::Ptr ConfigObject::GetProperties(void) const
return m_Properties;
}
void ConfigObject::SetTags(const Dictionary::Ptr& tags)
{
m_Tags = tags;
}
Dictionary::Ptr ConfigObject::GetTags(void) const
{
return m_Tags;
@ -88,7 +104,7 @@ void ConfigObject::SetCommitTimestamp(time_t ts)
time_t ConfigObject::GetCommitTimestamp(void) const
{
long value = false;
long value = 0;
GetProperties()->Get("__tx", &value);
return value;
}
@ -215,3 +231,89 @@ ScriptTask::Ptr ConfigObject::InvokeMethod(const string& method,
return task;
}
void ConfigObject::DumpObjects(const string& filename)
{
Logger::Write(LogInformation, "base", "Dumping program state to file '" + filename + "'");
ofstream fp;
fp.open(filename.c_str());
if (!fp)
throw runtime_error("Could not open retention.dat file");
FIFO::Ptr fifo = boost::make_shared<FIFO>();
BOOST_FOREACH(const ConfigObject::Ptr object, ConfigObject::GetAllObjects()) {
Dictionary::Ptr persistentObject = boost::make_shared<Dictionary>();
persistentObject->Set("properties", object->GetProperties());
persistentObject->Set("tags", object->GetTags());
Variant value = persistentObject;
string json = value.Serialize();
/* This is quite ugly, unfortunatelly Netstring requires an IOQueue object */
Netstring::WriteStringToIOQueue(fifo.get(), json);
size_t count;
while ((count = fifo->GetAvailableBytes()) > 0) {
char buffer[1024];
if (count > sizeof(buffer))
count = sizeof(buffer);
fifo->Read(buffer, count);
fp.write(buffer, count);
}
}
}
void ConfigObject::RestoreObjects(const string& filename)
{
assert(GetAllObjects()->Begin() == GetAllObjects()->End());
Logger::Write(LogInformation, "base", "Restoring program state from file '" + filename + "'");
std::ifstream fp;
fp.open(filename.c_str());
/* TODO: Fix this horrible mess. */
FIFO::Ptr fifo = boost::make_shared<FIFO>();
while (fp) {
char buffer[1024];
fp.read(buffer, sizeof(buffer));
fifo->Write(buffer, fp.gcount());
}
string message;
while (Netstring::ReadStringFromIOQueue(fifo.get(), &message)) {
Variant value = Variant::Deserialize(message);
if (!value.IsObjectType<Dictionary>())
throw runtime_error("JSON objects in the retention file must be dictionaries.");
Dictionary::Ptr persistentObject = value;
Dictionary::Ptr properties;
if (!persistentObject->Get("properties", &properties))
continue;
Dictionary::Ptr tags;
if (!persistentObject->Get("tags", &tags))
continue;
ConfigObject::Ptr object = boost::make_shared<ConfigObject>(properties);
if (!object->GetSource().empty()) {
/* restore replicated objects right away */
object->SetTags(tags);
object->Commit();
} else {
/* keep non-replicated objects until another config object with
* the same name is created (which is when we restore its tags) */
m_RetainedObjects[make_pair(object->GetType(), object->GetName())] = object;
}
}
}

View File

@ -40,7 +40,7 @@ public:
ConfigObject(Dictionary::Ptr properties, const Set::Ptr& container = Set::Ptr());
void SetProperties(Dictionary::Ptr config);
void SetProperties(const Dictionary::Ptr& config);
Dictionary::Ptr GetProperties(void) const;
template<typename T>
@ -49,6 +49,7 @@ public:
return GetProperties()->Get(key, value);
}
void SetTags(const Dictionary::Ptr& tags);
Dictionary::Ptr GetTags(void) const;
template<typename T>
@ -93,11 +94,16 @@ public:
static function<bool (ConfigObject::Ptr)> MakeTypePredicate(string type);
static void DumpObjects(const string& filename);
static void RestoreObjects(const string& filename);
private:
Set::Ptr m_Container;
Dictionary::Ptr m_Properties;
Dictionary::Ptr m_Tags;
static map<pair<string, string>, ConfigObject::Ptr> m_RetainedObjects;
void SetCommitTimestamp(time_t ts);
static bool TypeAndNameGetter(const ConfigObject::Ptr& object, pair<string, string> *key);

View File

@ -100,7 +100,9 @@ using std::pair;
using std::deque;
using std::stringstream;
using std::istream;
using std::ostream;
using std::ifstream;
using std::ofstream;
using std::exception;
@ -171,6 +173,7 @@ namespace tuples = boost::tuples;
#include "ringbuffer.h"
#include "timer.h"
#include "ioqueue.h"
#include "netstring.h"
#include "fifo.h"
#include "socket.h"
#include "tcpsocket.h"

View File

@ -17,7 +17,7 @@
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "i2-jsonrpc.h"
#include "i2-base.h"
using namespace icinga;

View File

@ -28,9 +28,9 @@ namespace icinga
*
* @see http://cr.yp.to/proto/netstrings.txt
*
* @ingroup jsonrpc
* @ingroup base
*/
class I2_JSONRPC_API Netstring
class I2_BASE_API Netstring
{
public:
static bool ReadStringFromIOQueue(IOQueue *queue, string *message);

View File

@ -75,7 +75,12 @@ public:
operator long(void) const
{
if (m_Value.type() != typeid(long)) {
if (m_Value.type() == typeid(double)) {
// TODO: log this?
return boost::get<double>(m_Value);
} else {
return boost::lexical_cast<long>(m_Value);
}
} else {
return boost::get<long>(m_Value);
}
@ -109,6 +114,9 @@ public:
template<typename T>
operator shared_ptr<T>(void) const
{
if (IsEmpty())
return shared_ptr<T>();
shared_ptr<T> object = dynamic_pointer_cast<T>(boost::get<Object::Ptr>(m_Value));
if (!object)

View File

@ -74,7 +74,7 @@ vector<ConfigItem::Ptr> ConfigCompiler::CompileFile(const string& path)
ifstream stream;
stream.open(path.c_str(), ifstream::in);
if (!stream.good())
if (!stream)
throw_exception(invalid_argument("Could not open config file: " + path));
Logger::Write(LogInformation, "dyn", "Compiling config file: " + path);

View File

@ -169,13 +169,13 @@ Global
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{EAD41628-BB96-4F99-9070-8A9676801295} = {4A1773FD-DDED-4952-8700-C898E890554A}
{2E6C1133-730F-4875-A72C-B455B1DD4C5C} = {4A1773FD-DDED-4952-8700-C898E890554A}
{EAD41628-BB96-4F99-9070-8A9676801295} = {4A1773FD-DDED-4952-8700-C898E890554A}
{38CE81CC-2660-4EF0-A936-4A337591DA3E} = {4A1773FD-DDED-4952-8700-C898E890554A}
{17C93245-8C20-4316-9573-1AE41D918C10} = {4A1773FD-DDED-4952-8700-C898E890554A}
{704DDD8E-9E6D-4C22-80BD-6DE10F3A5E1C} = {4A1773FD-DDED-4952-8700-C898E890554A}
{2BD1C70C-43DB-4F44-B66B-67CF5C7044AA} = {4A1773FD-DDED-4952-8700-C898E890554A}
{D02A349B-BAF7-41FB-86FF-B05BA05FE578} = {4A1773FD-DDED-4952-8700-C898E890554A}
{E58F1DA7-B723-412B-B2B7-7FF58E2A944E} = {4A1773FD-DDED-4952-8700-C898E890554A}
{2BD1C70C-43DB-4F44-B66B-67CF5C7044AA} = {4A1773FD-DDED-4952-8700-C898E890554A}
{704DDD8E-9E6D-4C22-80BD-6DE10F3A5E1C} = {4A1773FD-DDED-4952-8700-C898E890554A}
{38CE81CC-2660-4EF0-A936-4A337591DA3E} = {4A1773FD-DDED-4952-8700-C898E890554A}
EndGlobalSection
EndGlobal

View File

@ -42,6 +42,14 @@ IcingaApplication::IcingaApplication(void)
*/
int IcingaApplication::Main(const vector<string>& args)
{
/* restore the previous program state */
ConfigObject::RestoreObjects("retention.dat");
m_RetentionTimer = boost::make_shared<Timer>();
m_RetentionTimer->SetInterval(60);
m_RetentionTimer->OnTimerExpired.connect(boost::bind(&IcingaApplication::RetentionTimerHandler, this));
m_RetentionTimer->Start();
/* register handler for 'log' config objects */
static ConfigObject::Set::Ptr logObjects = boost::make_shared<ConfigObject::Set>(ConfigObject::GetAllObjects(), ConfigObject::MakeTypePredicate("log"));
logObjects->OnObjectAdded.connect(boost::bind(&IcingaApplication::NewLogHandler, this, _2));
@ -213,6 +221,10 @@ int IcingaApplication::Main(const vector<string>& args)
return EXIT_SUCCESS;
}
void IcingaApplication::RetentionTimerHandler(void) {
ConfigObject::DumpObjects("retention.dat");
}
void IcingaApplication::NewComponentHandler(const ConfigObject::Ptr& object)
{
/* don't allow replicated config objects */

View File

@ -61,6 +61,10 @@ private:
time_t m_StartTime;
Timer::Ptr m_RetentionTimer;
void RetentionTimerHandler(void);
void NewComponentHandler(const ConfigObject::Ptr& object);
void DeletedComponentHandler(const ConfigObject::Ptr& object);

View File

@ -39,7 +39,6 @@
#include "messagepart.h"
#include "requestmessage.h"
#include "responsemessage.h"
#include "netstring.h"
#include "jsonrpcclient.h"
#include "jsonrpcserver.h"

View File

@ -17,7 +17,6 @@
<ClInclude Include="responsemessage.h" />
<ClInclude Include="jsonrpcserver.h" />
<ClInclude Include="messagepart.h" />
<ClInclude Include="netstring.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="i2-jsonrpc.cpp">
@ -29,7 +28,6 @@
<ClCompile Include="responsemessage.cpp" />
<ClCompile Include="jsonrpcserver.cpp" />
<ClCompile Include="messagepart.cpp" />
<ClCompile Include="netstring.cpp" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{8DD52FAC-ECEE-48C2-B266-E7C47ED485F8}</ProjectGuid>

View File

@ -13,9 +13,6 @@
<ClCompile Include="messagepart.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="netstring.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="requestmessage.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
@ -36,9 +33,6 @@
<ClInclude Include="messagepart.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="netstring.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="requestmessage.h">
<Filter>Headerdateien</Filter>
</ClInclude>