Implement support for tracking dependencies between config objects

refs #9096
This commit is contained in:
Gunnar Beutner 2015-08-25 13:53:43 +02:00
parent 7eca257784
commit f45f6ccd82
53 changed files with 452 additions and 152 deletions

View File

@ -26,7 +26,7 @@ set(base_SOURCES
application.cpp application.thpp application-version.cpp array.cpp
array-script.cpp boolean.cpp boolean-script.cpp console.cpp context.cpp
convert.cpp debuginfo.cpp dictionary.cpp dictionary-script.cpp
configobject.cpp configobject.thpp configobject-script.cpp configtype.cpp
configobject.cpp configobject.thpp configobject-script.cpp configtype.cpp dependencygraph.cpp
exception.cpp fifo.cpp filelogger.cpp filelogger.thpp initialize.cpp json.cpp
json-script.cpp loader.cpp logger.cpp logger.thpp math-script.cpp
netstring.cpp networkstream.cpp number.cpp number-script.cpp object.cpp

View File

@ -102,7 +102,7 @@ void Application::Stop(void)
} else
ClosePidFile(true);
ConfigObject::Stop();
ObjectImpl<Application>::Stop();
}
Application::~Application(void)

View File

@ -231,6 +231,8 @@ void ConfigObject::Unregister(void)
void ConfigObject::Start(void)
{
ObjectImpl<ConfigObject>::Start();
ASSERT(!OwnsLock());
ObjectLock olock(this);
@ -260,6 +262,8 @@ void ConfigObject::Activate(void)
void ConfigObject::Stop(void)
{
ObjectImpl<ConfigObject>::Stop();
ASSERT(!OwnsLock());
ObjectLock olock(this);
@ -501,5 +505,7 @@ void ConfigObject::DumpModifiedAttributes(const boost::function<void(const Confi
ConfigObject::Ptr ConfigObject::GetObject(const String& type, const String& name)
{
ConfigType::Ptr dtype = ConfigType::GetByName(type);
if (!dtype)
return ConfigObject::Ptr();
return dtype->GetObject(name);
}

View File

@ -64,8 +64,8 @@ public:
void Deactivate(void);
void SetAuthority(bool authority);
virtual void Start(void);
virtual void Stop(void);
virtual void Start(void) override;
virtual void Stop(void) override;
virtual void Pause(void);
virtual void Resume(void);
@ -83,6 +83,8 @@ public:
return static_pointer_cast<T>(object);
}
static ConfigObject::Ptr GetObject(const String& type, const String& name);
static void DumpObjects(const String& filename, int attributeTypes = FAState);
static void RestoreObjects(const String& filename, int attributeTypes = FAState);
static void StopObjects(void);
@ -95,7 +97,6 @@ protected:
explicit ConfigObject(void);
private:
static ConfigObject::Ptr GetObject(const String& type, const String& name);
static void RestoreObject(const String& message, int attributeTypes);
};

View File

@ -55,6 +55,12 @@ public:
m_DebugInfo = di;
}
inline virtual void Start(void)
{ }
inline virtual void Stop(void)
{ }
private:
DebugInfo m_DebugInfo;
};

View File

@ -0,0 +1,55 @@
/******************************************************************************
* 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 "base/dependencygraph.hpp"
#include <boost/foreach.hpp>
using namespace icinga;
boost::mutex DependencyGraph::m_Mutex;
std::map<Object *, std::map<Object *, int> > DependencyGraph::m_Dependencies;
void DependencyGraph::AddDependency(Object *parent, Object *child)
{
boost::mutex::scoped_lock lock(m_Mutex);
m_Dependencies[child][parent]++;
}
void DependencyGraph::RemoveDependency(Object *parent, Object *child)
{
boost::mutex::scoped_lock lock(m_Mutex);
m_Dependencies[child][parent]--;
}
std::vector<Object::Ptr> DependencyGraph::GetParents(const Object::Ptr& child)
{
std::vector<Object::Ptr> objects;
boost::mutex::scoped_lock lock(m_Mutex);
std::map<Object *, std::map<Object *, int> >::const_iterator it = m_Dependencies.find(child.get());
if (it != m_Dependencies.end()) {
typedef std::pair<Object *, int> kv_pair;
BOOST_FOREACH(const kv_pair& kv, it->second) {
objects.push_back(kv.first);
}
}
return objects;
}

View File

@ -0,0 +1,50 @@
/******************************************************************************
* 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. *
******************************************************************************/
#ifndef DEPENDENCYGRAPH_H
#define DEPENDENCYGRAPH_H
#include "base/i2-base.hpp"
#include "base/object.hpp"
#include <map>
namespace icinga {
/**
* A graph that tracks dependencies between objects.
*
* @ingroup base
*/
class I2_BASE_API DependencyGraph
{
public:
static void AddDependency(Object *parent, Object *child);
static void RemoveDependency(Object *parent, Object *child);
static std::vector<Object::Ptr> GetParents(const Object::Ptr& child);
private:
DependencyGraph(void);
static boost::mutex m_Mutex;
static std::map<Object *, std::map<Object *, int> > m_Dependencies;
};
}
#endif /* DEPENDENCYGRAPH_H */

View File

@ -46,7 +46,7 @@ void FileLogger::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr&)
*/
void FileLogger::Start(void)
{
StreamLogger::Start();
ObjectImpl<FileLogger>::Start();
ReopenLogFile();

View File

@ -54,7 +54,7 @@ void Logger::StaticInitialize(void)
*/
void Logger::Start(void)
{
ConfigObject::Start();
ObjectImpl<Logger>::Start();
boost::mutex::scoped_lock lock(m_Mutex);
m_Loggers.insert(this);
@ -67,7 +67,7 @@ void Logger::Stop(void)
m_Loggers.erase(this);
}
ConfigObject::Stop();
ObjectImpl<Logger>::Stop();
}
std::set<Logger::Ptr> Logger::GetLoggers(void)

View File

@ -92,6 +92,11 @@ Value Object::GetField(int id) const
BOOST_THROW_EXCEPTION(std::runtime_error("Invalid field ID."));
}
void Object::Validate(int types, const ValidationUtils& utils)
{
/* Nothing to do here. */
}
void Object::ValidateField(int id, const Value& value, const ValidationUtils& utils)
{
/* Nothing to do here. */

View File

@ -103,6 +103,8 @@ public:
virtual intrusive_ptr<Type> GetReflectionType(void) const;
virtual void Validate(int types, const ValidationUtils& utils);
virtual void SetField(int id, const Value& value, bool suppress_events = false, const Value& cookie = Empty);
virtual Value GetField(int id) const;
virtual void ValidateField(int id, const Value& value, const ValidationUtils& utils);

View File

@ -26,6 +26,7 @@
#include "base/objectlock.hpp"
#include "base/configtype.hpp"
#include "base/application.hpp"
#include "base/dependencygraph.hpp"
#include <boost/foreach.hpp>
#include <boost/regex.hpp>
#include <algorithm>
@ -57,6 +58,7 @@ REGISTER_SAFE_SCRIPTFUNCTION(get_time, &Utility::GetTime);
REGISTER_SAFE_SCRIPTFUNCTION(basename, &Utility::BaseName);
REGISTER_SAFE_SCRIPTFUNCTION(dirname, &Utility::DirName);
REGISTER_SAFE_SCRIPTFUNCTION(msi_get_component_path, &ScriptUtils::MsiGetComponentPathShim);
REGISTER_SAFE_SCRIPTFUNCTION(track_parents, &ScriptUtils::TrackParents);
String ScriptUtils::CastString(const Value& value)
{
@ -304,3 +306,9 @@ String ScriptUtils::MsiGetComponentPathShim(const String& component)
return String();
#endif /* _WIN32 */
}
Array::Ptr ScriptUtils::TrackParents(const Object::Ptr& child)
{
return Array::FromVector(DependencyGraph::GetParents(child));
}

View File

@ -51,6 +51,7 @@ public:
static Array::Ptr GetObjects(const Type::Ptr& type);
static void Assert(const Value& arg);
static String MsiGetComponentPathShim(const String& component);
static Array::Ptr TrackParents(const Object::Ptr& parent);
private:
ScriptUtils(void);

View File

@ -39,7 +39,7 @@ StreamLogger::StreamLogger(void)
void StreamLogger::Stop(void)
{
Logger::Stop();
ObjectImpl<StreamLogger>::Stop();
// make sure we flush the log data on shutdown, even if we don't call the destructor
if (m_Stream)

View File

@ -159,9 +159,9 @@ int TypeType::GetFieldId(const String& name) const
Field TypeType::GetFieldInfo(int id) const
{
if (id == 0)
return Field(0, "Object", "prototype", NULL, 0);
return Field(0, "Object", "prototype", NULL, 0, 0);
else if (id == 1)
return Field(1, "Object", "base", NULL, 0);
return Field(1, "Object", "base", NULL, 0, 0);
throw std::runtime_error("Invalid field ID.");
}

View File

@ -49,9 +49,10 @@ struct Field
const char *Name;
const char *RefTypeName;
int Attributes;
int ArrayRank;
Field(int id, const char *type, const char *name, const char *reftype, int attributes)
: ID(id), TypeName(type), Name(name), RefTypeName(reftype), Attributes(attributes)
Field(int id, const char *type, const char *name, const char *reftype, int attributes, int arrayRank)
: ID(id), TypeName(type), Name(name), RefTypeName(reftype), Attributes(attributes), ArrayRank(arrayRank)
{ }
};

View File

@ -74,7 +74,7 @@ void CheckerComponent::OnConfigLoaded(void)
void CheckerComponent::Start(void)
{
ConfigObject::Start();
ObjectImpl<CheckerComponent>::Start();
m_Thread = boost::thread(boost::bind(&CheckerComponent::CheckThreadProc, this));
@ -97,7 +97,7 @@ void CheckerComponent::Stop(void)
m_ResultTimer->Stop();
m_Thread.join();
ConfigObject::Stop();
ObjectImpl<CheckerComponent>::Stop();
}
void CheckerComponent::CheckThreadProc(void)

View File

@ -59,7 +59,7 @@ void CompatLogger::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr&)
*/
void CompatLogger::Start(void)
{
ConfigObject::Start();
ObjectImpl<CompatLogger>::Start();
Checkable::OnNewCheckResult.connect(bind(&CompatLogger::CheckResultHandler, this, _1, _2));
Checkable::OnNotificationSentToUser.connect(bind(&CompatLogger::NotificationSentHandler, this, _1, _2, _3, _4, _5, _6, _7, _8));

View File

@ -48,7 +48,7 @@ void ExternalCommandListener::StatsFunc(const Dictionary::Ptr& status, const Arr
*/
void ExternalCommandListener::Start(void)
{
ConfigObject::Start();
ObjectImpl<ExternalCommandListener>::Start();
#ifndef _WIN32
m_CommandThread = boost::thread(boost::bind(&ExternalCommandListener::CommandPipeThread, this, GetCommandPath()));

View File

@ -72,7 +72,7 @@ void StatusDataWriter::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr
*/
void StatusDataWriter::Start(void)
{
ConfigObject::Start();
ObjectImpl<StatusDataWriter>::Start();
m_StatusTimer = new Timer();
m_StatusTimer->SetInterval(GetUpdateInterval());

View File

@ -58,7 +58,7 @@ void DbConnection::OnConfigLoaded(void)
void DbConnection::Start(void)
{
ConfigObject::Start();
ObjectImpl<DbConnection>::Start();
DbObject::OnQuery.connect(boost::bind(&DbConnection::ExecuteQuery, this, _1));
ConfigObject::OnActiveChanged.connect(boost::bind(&DbConnection::UpdateObject, this, _1));

View File

@ -35,7 +35,7 @@ REGISTER_APIFUNCTION(HelloWorld, demo, &Demo::DemoMessageHandler);
*/
void Demo::Start(void)
{
ConfigObject::Start();
ObjectImpl<Demo>::Start();
m_DemoTimer = new Timer();
m_DemoTimer->SetInterval(5);

View File

@ -47,7 +47,7 @@ void Checkable::Start(void)
if (GetNextCheck() < now + 300)
UpdateNextCheck();
ConfigObject::Start();
ObjectImpl<Checkable>::Start();
}
void Checkable::OnStateLoaded(void)

View File

@ -82,7 +82,7 @@ void Dependency::OnConfigLoaded(void)
void Dependency::OnAllConfigLoaded(void)
{
ConfigObject::OnAllConfigLoaded();
ObjectImpl<Dependency>::OnAllConfigLoaded();
Host::Ptr childHost = Host::GetByName(GetChildHostName());
@ -125,7 +125,7 @@ void Dependency::OnAllConfigLoaded(void)
void Dependency::Stop(void)
{
ConfigObject::Stop();
ObjectImpl<Dependency>::Stop();
GetChild()->RemoveDependency(this);
GetParent()->RemoveReverseDependency(this);
@ -232,3 +232,4 @@ void Dependency::ValidateStates(const Array::Ptr& value, const ValidationUtils&
if (!GetParentServiceName().IsEmpty() && (sfilter & ~(StateFilterOK | StateFilterWarning | StateFilterCritical | StateFilterUnknown)) != 0)
BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("states"), "State filter is invalid for service dependency."));
}

View File

@ -19,6 +19,7 @@
#include "icinga/customvarobject.hpp"
#include "icinga/checkable.hpp"
#impl_include "icinga/service.hpp"
library icinga;
@ -40,14 +41,38 @@ class Dependency : CustomVarObject < DependencyNameComposer
load_after Service;
[config, required] name(Host) child_host_name;
[config] String child_service_name;
[config] String child_service_name {
track {{{
if (!oldValue.IsEmpty()) {
Service::Ptr service = Service::GetByNamePair(GetParentHostName(), oldValue);
DependencyGraph::RemoveDependency(this, service.get());
}
if (!newValue.IsEmpty()) {
Service::Ptr service = Service::GetByNamePair(GetParentHostName(), newValue);
DependencyGraph::RemoveDependency(this, service.get());
}
}}}
};
[config, required] name(Host) parent_host_name;
[config] String parent_service_name;
[config] String parent_service_name {
track {{{
if (!oldValue.IsEmpty()) {
Service::Ptr service = Service::GetByNamePair(GetParentHostName(), oldValue);
DependencyGraph::RemoveDependency(this, service.get());
}
if (!newValue.IsEmpty()) {
Service::Ptr service = Service::GetByNamePair(GetParentHostName(), newValue);
DependencyGraph::RemoveDependency(this, service.get());
}
}}}
};
[config] name(TimePeriod) period (PeriodRaw);
[config] Array::Ptr states;
[config] array(double) states;
int state_filter_real (StateFilter);
[config] bool ignore_soft_states {
@ -60,10 +85,4 @@ class Dependency : CustomVarObject < DependencyNameComposer
};
};
validator Dependency {
Array states {
Number "*";
};
};
}

View File

@ -36,7 +36,7 @@ REGISTER_TYPE(Host);
void Host::OnAllConfigLoaded(void)
{
Checkable::OnAllConfigLoaded();
ObjectImpl<Host>::OnAllConfigLoaded();
HostGroup::EvaluateObjectRules(this);
@ -73,7 +73,7 @@ void Host::CreateChildObjects(const Type::Ptr& childType)
void Host::Stop(void)
{
Checkable::Stop();
ObjectImpl<Host>::Stop();
Array::Ptr groups = GetGroups();

View File

@ -27,7 +27,7 @@ namespace icinga
class Host : Checkable
{
[config] Array::Ptr groups {
[config] array(name(HostGroup)) groups {
default {{{ return new Array(); }}}
};
@ -55,10 +55,4 @@ class Host : Checkable
};
validator Host {
Array groups {
name(HostGroup) "*";
};
};
}

View File

@ -35,16 +35,10 @@ class HostGroup : CustomVarObject
}}}
};
[config] Array::Ptr groups;
[config] array(name(HostGroup)) groups;
[config] String notes;
[config] String notes_url;
[config] String action_url;
};
validator HostGroup {
Array groups {
name(HostGroup) "*";
};
};
}

View File

@ -58,7 +58,7 @@ void IcingaStatusWriter::StatsFunc(const Dictionary::Ptr& status, const Array::P
*/
void IcingaStatusWriter::Start(void)
{
ConfigObject::Start();
ObjectImpl<IcingaStatusWriter>::Start();
m_StatusTimer = new Timer();
m_StatusTimer->SetInterval(GetUpdateInterval());

View File

@ -115,7 +115,7 @@ void Notification::OnAllConfigLoaded(void)
void Notification::Start(void)
{
ConfigObject::Start();
ObjectImpl<Notification>::Start();
Checkable::Ptr obj = GetCheckable();
@ -125,7 +125,7 @@ void Notification::Start(void)
void Notification::Stop(void)
{
ConfigObject::Stop();
ObjectImpl<Notification>::Stop();
Checkable::Ptr obj = GetCheckable();
@ -676,3 +676,4 @@ Endpoint::Ptr Notification::GetCommandEndpoint(void) const
{
return Endpoint::GetByName(GetCommandEndpointRaw());
}

View File

@ -18,6 +18,7 @@
******************************************************************************/
#include "icinga/customvarobject.hpp"
#impl_include "icinga/service.hpp"
library icinga;
@ -43,15 +44,27 @@ class Notification : CustomVarObject < NotificationNameComposer
default {{{ return 1800; }}}
};
[config] name(TimePeriod) period (PeriodRaw);
[config, protected] Array::Ptr users (UsersRaw);
[config, protected] Array::Ptr user_groups (UserGroupsRaw);
[config, protected] array(name(User)) users (UsersRaw);
[config, protected] array(name(UserGroup)) user_groups (UserGroupsRaw);
[config] Dictionary::Ptr times;
[config] Array::Ptr types;
[config] array(double) types;
int type_filter_real (TypeFilter);
[config] Array::Ptr states;
[config] array(double) states;
int state_filter_real (StateFilter);
[config, protected, required] name(Host) host_name;
[config, protected] String service_name;
[config, protected] String service_name {
track {{{
if (!oldValue.IsEmpty()) {
Service::Ptr service = Service::GetByNamePair(GetHostName(), oldValue);
DependencyGraph::RemoveDependency(this, service.get());
}
if (!newValue.IsEmpty()) {
Service::Ptr service = Service::GetByNamePair(GetHostName(), newValue);
DependencyGraph::RemoveDependency(this, service.get());
}
}}}
};
[state] Array::Ptr notified_users {
default {{{ return new Array(); }}}
@ -66,26 +79,10 @@ class Notification : CustomVarObject < NotificationNameComposer
};
validator Notification {
Array users {
name(User) "*";
};
Array user_groups {
name(UserGroup) "*";
};
Dictionary times {
Number begin;
Number end;
};
Array types {
Number "*";
};
Array states {
Number "*";
};
};
}

View File

@ -90,7 +90,7 @@ void ScheduledDowntime::StaticInitialize(void)
void ScheduledDowntime::OnAllConfigLoaded(void)
{
CustomVarObject::OnAllConfigLoaded();
ObjectImpl<ScheduledDowntime>::OnAllConfigLoaded();
if (!GetCheckable())
BOOST_THROW_EXCEPTION(ScriptError("ScheduledDowntime '" + GetName() + "' references a host/service which doesn't exist.", GetDebugInfo()));
@ -98,7 +98,7 @@ void ScheduledDowntime::OnAllConfigLoaded(void)
void ScheduledDowntime::Start(void)
{
ConfigObject::Start();
ObjectImpl<ScheduledDowntime>::Start();
CreateNextDowntime();
}
@ -231,3 +231,4 @@ void ScheduledDowntime::ValidateRanges(const Dictionary::Ptr& value, const Valid
}
}
}

View File

@ -18,6 +18,7 @@
******************************************************************************/
#include "icinga/customvarobject.hpp"
#impl_include "icinga/service.hpp"
library icinga;
@ -39,7 +40,19 @@ class ScheduledDowntime : CustomVarObject < ScheduledDowntimeNameComposer
load_after Service;
[config, protected, required] name(Host) host_name;
[config, protected] String service_name;
[config, protected] String service_name {
track {{{
if (!oldValue.IsEmpty()) {
Service::Ptr service = Service::GetByNamePair(GetHostName(), oldValue);
DependencyGraph::RemoveDependency(this, service.get());
}
if (!newValue.IsEmpty()) {
Service::Ptr service = Service::GetByNamePair(GetHostName(), newValue);
DependencyGraph::RemoveDependency(this, service.get());
}
}}}
};
[config, required] String author;
[config, required] String comment;

View File

@ -61,7 +61,7 @@ Dictionary::Ptr ServiceNameComposer::ParseName(const String& name) const
void Service::OnAllConfigLoaded(void)
{
Checkable::OnAllConfigLoaded();
ObjectImpl<Service>::OnAllConfigLoaded();
m_Host = Host::GetByName(GetHostName());

View File

@ -40,7 +40,7 @@ class Service : Checkable < ServiceNameComposer
{
load_after Host;
[config] Array::Ptr groups {
[config] array(name(ServiceGroup)) groups {
default {{{ return new Array(); }}}
};
@ -70,10 +70,4 @@ class Service : Checkable < ServiceNameComposer
};
};
validator Service {
Array groups {
name(ServiceGroup) "*";
};
};
}

View File

@ -35,16 +35,10 @@ class ServiceGroup : CustomVarObject
}}}
};
[config] Array::Ptr groups;
[config] array(name(ServiceGroup)) groups;
[config] String notes;
[config] String notes_url;
[config] String action_url;
};
validator ServiceGroup {
Array groups {
name(ServiceGroup) "*";
};
};
}

View File

@ -46,7 +46,7 @@ void TimePeriod::StaticInitialize(void)
void TimePeriod::Start(void)
{
ConfigObject::Start();
ObjectImpl<TimePeriod>::Start();
/* Pre-fill the time period for the next 24 hours. */
double now = Utility::GetTime();

View File

@ -32,7 +32,7 @@ REGISTER_TYPE(User);
void User::OnConfigLoaded(void)
{
ConfigObject::OnConfigLoaded();
ObjectImpl<User>::OnConfigLoaded();
SetTypeFilter(FilterArrayToInt(GetTypes(), ~0));
SetStateFilter(FilterArrayToInt(GetStates(), ~0));
@ -40,7 +40,7 @@ void User::OnConfigLoaded(void)
void User::OnAllConfigLoaded(void)
{
ConfigObject::OnAllConfigLoaded();
ObjectImpl<User>::OnAllConfigLoaded();
UserGroup::EvaluateObjectRules(this);
@ -62,7 +62,7 @@ void User::OnAllConfigLoaded(void)
void User::Stop(void)
{
ConfigObject::Stop();
ObjectImpl<User>::Stop();
Array::Ptr groups = GetGroups();

View File

@ -35,13 +35,13 @@ class User : CustomVarObject
return m_DisplayName;
}}}
};
[config] Array::Ptr groups {
[config] array(name(UserGroup)) groups {
default {{{ return new Array(); }}}
};
[config] name(TimePeriod) period (PeriodRaw);
[config] Array::Ptr types;
[config] array(double) types;
int type_filter_real (TypeFilter);
[config] Array::Ptr states;
[config] array(double) states;
int state_filter_real (StateFilter);
[config] String email;
@ -54,18 +54,4 @@ class User : CustomVarObject
[state] double last_notification;
};
validator User {
Array groups {
name(UserGroup) "*";
};
Array types {
Number "*";
};
Array states {
Number "*";
};
};
}

View File

@ -35,13 +35,7 @@ class UserGroup : CustomVarObject
}}}
};
[config] Array::Ptr groups;
};
validator UserGroup {
Array groups {
name(UserGroup) "*";
};
[config] array(name(UserGroup)) groups;
};
}

View File

@ -64,7 +64,7 @@ void LivestatusListener::StatsFunc(const Dictionary::Ptr& status, const Array::P
*/
void LivestatusListener::Start(void)
{
ConfigObject::Start();
ObjectImpl<LivestatusListener>::Start();
if (GetSocketType() == "tcp") {
TcpSocket::Ptr socket = new TcpSocket();
@ -121,7 +121,7 @@ void LivestatusListener::Start(void)
void LivestatusListener::Stop(void)
{
ConfigObject::Stop();
ObjectImpl<LivestatusListener>::Stop();
m_Listener->Close();

View File

@ -51,7 +51,7 @@ void NotificationComponent::StatsFunc(const Dictionary::Ptr& status, const Array
*/
void NotificationComponent::Start(void)
{
ConfigObject::Start();
ObjectImpl<NotificationComponent>::Start();
Checkable::OnNotificationsRequested.connect(boost::bind(&NotificationComponent::SendNotificationsHandler, this, _1,
_2, _3, _4, _5));

View File

@ -40,7 +40,7 @@ REGISTER_TYPE(GelfWriter);
void GelfWriter::Start(void)
{
ConfigObject::Start();
ObjectImpl<GelfWriter>::Start();
m_ReconnectTimer = new Timer();
m_ReconnectTimer->SetInterval(10);

View File

@ -60,7 +60,7 @@ void GraphiteWriter::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr&)
void GraphiteWriter::Start(void)
{
ConfigObject::Start();
ObjectImpl<GraphiteWriter>::Start();
m_ReconnectTimer = new Timer();
m_ReconnectTimer->SetInterval(10);

View File

@ -60,7 +60,7 @@ void OpenTsdbWriter::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr&)
void OpenTsdbWriter::Start(void)
{
ConfigObject::Start();
ObjectImpl<OpenTsdbWriter>::Start();
m_ReconnectTimer = new Timer();
m_ReconnectTimer->SetInterval(10);

View File

@ -51,7 +51,7 @@ void PerfdataWriter::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr&)
void PerfdataWriter::Start(void)
{
ConfigObject::Start();
ObjectImpl<PerfdataWriter>::Start();
Checkable::OnNewCheckResult.connect(boost::bind(&PerfdataWriter::CheckResultHandler, this, _1, _2));

View File

@ -102,7 +102,7 @@ void ApiListener::Start(void)
return;
}
ConfigObject::Start();
ObjectImpl<ApiListener>::Start();
{
boost::mutex::scoped_lock(m_LogLock);

View File

@ -21,6 +21,8 @@
#include "remote/httputility.hpp"
#include "remote/filterutility.hpp"
#include "base/serializer.hpp"
#include "base/dependencygraph.hpp"
#include "base/configtype.hpp"
#include <boost/algorithm/string.hpp>
#include <set>
@ -72,11 +74,16 @@ bool StatusQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& re
}
BOOST_FOREACH(const ConfigObject::Ptr& obj, objs) {
Dictionary::Ptr result1 = new Dictionary();
results->Add(result1);
Dictionary::Ptr resultAttrs = new Dictionary();
result1->Set("attrs", resultAttrs);
BOOST_FOREACH(const String& joinType, joinTypes) {
String prefix = joinType;
boost::algorithm::to_lower(prefix);
Dictionary::Ptr result1 = new Dictionary();
for (int fid = 0; fid < type->GetFieldCount(); fid++) {
Field field = type->GetFieldInfo(fid);
String aname = prefix + "." + field.Name;
@ -85,9 +92,23 @@ bool StatusQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& re
Value val = static_cast<Object::Ptr>(obj)->GetField(fid);
Value sval = Serialize(val, FAConfig | FAState);
result1->Set(aname, sval);
resultAttrs->Set(aname, sval);
}
results->Add(result1);
}
Array::Ptr used_by = new Array();
result1->Set("used_by", used_by);
BOOST_FOREACH(const Object::Ptr& pobj, DependencyGraph::GetParents((obj))) {
ConfigObject::Ptr configObj = dynamic_pointer_cast<ConfigObject>(pobj);
if (!configObj)
continue;
Dictionary::Ptr refInfo = new Dictionary();
refInfo->Set("type", configObj->GetType()->GetName());
refInfo->Set("name", configObj->GetName());
used_by->Add(refInfo);
}
}

View File

@ -27,14 +27,8 @@ namespace icinga
class Zone : ConfigObject
{
[config] name(Zone) parent (ParentRaw);
[config] Array::Ptr endpoints (EndpointsRaw);
[config] array(name(Endpoint)) endpoints (EndpointsRaw);
[config] bool global;
};
validator Zone {
Array endpoints {
name(Endpoint) "*";
};
};
}

View File

@ -130,6 +130,7 @@ static char *lb_steal(lex_buf *lb)
[ \t\r\n] /* ignore whitespace */
#include { return T_INCLUDE; }
#impl_include { return T_IMPL_INCLUDE; }
class { return T_CLASS; }
namespace { return T_NAMESPACE; }
code { return T_CODE; }
@ -147,9 +148,11 @@ no_storage { yylval->num = FANoStorage; return T_FIELD_ATTRIBUTE; }
validator { return T_VALIDATOR; }
required { return T_REQUIRED; }
name { return T_NAME; }
array { return T_ARRAY; }
default { yylval->num = FTDefault; return T_FIELD_ACCESSOR_TYPE; }
get { yylval->num = FTGet; return T_FIELD_ACCESSOR_TYPE; }
set { yylval->num = FTSet; return T_FIELD_ACCESSOR_TYPE; }
track { yylval->num = FTTrack; return T_FIELD_ACCESSOR_TYPE; }
\"[^\"]+\" { yylval->text = strdup(yytext + 1); yylval->text[strlen(yylval->text) - 1] = '\0'; return T_STRING; }
\<[^>]+\> { yylval->text = strdup(yytext + 1); yylval->text[strlen(yylval->text) - 1] = '\0'; return T_ANGLE_STRING; }
[a-zA-Z_][:a-zA-Z0-9\-_]* { yylval->text = strdup(yytext); return T_IDENTIFIER; }

View File

@ -56,7 +56,8 @@ using namespace icinga;
Validator *validator;
}
%token T_INCLUDE "include (T_INCLUDE)"
%token T_INCLUDE "#include (T_INCLUDE)"
%token T_IMPL_INCLUDE "#impl_include (T_IMPL_INCLUDE)"
%token T_CLASS "class (T_CLASS)"
%token T_CODE "code (T_CODE)"
%token T_LOAD_AFTER "load_after (T_LOAD_AFTER)"
@ -65,6 +66,7 @@ using namespace icinga;
%token T_VALIDATOR "validator (T_VALIDATOR)"
%token T_REQUIRED "required (T_REQUIRED)"
%token T_NAME "name (T_NAME)"
%token T_ARRAY "array (T_ARRAY)"
%token T_STRING "string (T_STRING)"
%token T_ANGLE_STRING "angle_string (T_ANGLE_STRING)"
%token T_FIELD_ATTRIBUTE "field_attribute (T_FIELD_ATTRIBUTE)"
@ -83,6 +85,8 @@ using namespace icinga;
%type <text> type_base_specifier
%type <text> include
%type <text> angle_include
%type <text> impl_include
%type <text> angle_impl_include
%type <text> code
%type <num> T_FIELD_ATTRIBUTE
%type <num> field_attribute
@ -147,6 +151,16 @@ statement: include
context->HandleAngleInclude($1, yylloc);
std::free($1);
}
| impl_include
{
context->HandleImplInclude($1, yylloc);
std::free($1);
}
| angle_impl_include
{
context->HandleAngleImplInclude($1, yylloc);
std::free($1);
}
| class
{
context->HandleClass(*$1, yylloc);
@ -178,6 +192,18 @@ angle_include: T_INCLUDE T_ANGLE_STRING
}
;
impl_include: T_IMPL_INCLUDE T_STRING
{
$$ = $2;
}
;
angle_impl_include: T_IMPL_INCLUDE T_ANGLE_STRING
{
$$ = $2;
}
;
namespace: T_NAMESPACE identifier '{'
{
context->HandleNamespaceBegin($2, yylloc);
@ -290,8 +316,14 @@ field_type: identifier
$$ = new FieldType();
$$->IsName = true;
$$->TypeName = $3;
$$->ArrayRank = 0;
free($3);
}
| T_ARRAY '(' field_type ')'
{
$$ = $3;
$$->ArrayRank++;
}
;
class_field: field_attribute_list field_type identifier alternative_name_specifier field_accessor_list ';'
@ -328,7 +360,10 @@ class_field: field_attribute_list field_type identifier alternative_name_specifi
case FTDefault:
field->DefaultAccessor = it->Accessor;
break;
}
case FTTrack:
field->TrackAccessor = it->Accessor;
break;
}
}
delete $5;

View File

@ -73,6 +73,16 @@ void ClassCompiler::HandleAngleInclude(const std::string& path, const ClassDebug
m_Header << "#include <" << path << ">" << std::endl << std::endl;
}
void ClassCompiler::HandleImplInclude(const std::string& path, const ClassDebugInfo&)
{
m_Impl << "#include \"" << path << "\"" << std::endl << std::endl;
}
void ClassCompiler::HandleAngleImplInclude(const std::string& path, const ClassDebugInfo&)
{
m_Impl << "#include <" << path << ">" << std::endl << std::endl;
}
void ClassCompiler::HandleNamespaceBegin(const std::string& name, const ClassDebugInfo&)
{
m_Header << "namespace " << name << std::endl
@ -356,7 +366,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&)
nameref = "NULL";
m_Impl << "\t\t" << "case " << num << ":" << std::endl
<< "\t\t\t" << "return Field(" << num << ", \"" << ftype << "\", \"" << it->Name << "\", " << nameref << ", " << it->Attributes << ");" << std::endl;
<< "\t\t\t" << "return Field(" << num << ", \"" << ftype << "\", \"" << it->Name << "\", " << nameref << ", " << it->Attributes << ", " << it->Type.ArrayRank << ");" << std::endl;
num++;
}
@ -454,7 +464,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&)
m_Impl << "template class ObjectImpl<" << klass.Name << ">;" << std::endl << std::endl;
/* Validate */
m_Header << "\t" << "virtual void Validate(int types, const ValidationUtils& utils);" << std::endl;
m_Header << "\t" << "virtual void Validate(int types, const ValidationUtils& utils) override;" << std::endl;
m_Impl << "void ObjectImpl<" << klass.Name << ">::Validate(int types, const ValidationUtils& utils)" << std::endl
<< "{" << std::endl;
@ -488,10 +498,21 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&)
}
if (field.Type.IsName) {
m_Impl << "\t" << "String ref = value;" << std::endl
<< "\t" << "if (!ref.IsEmpty() && !utils.ValidateName(\"" << field.Type.TypeName << "\", ref))" << std::endl
if (field.Type.ArrayRank > 0) {
m_Impl << "\t" << "if (value) {" << std::endl
<< "\t\t" << "ObjectLock olock(value);" << std::endl
<< "\t\t" << "BOOST_FOREACH(const String& ref, value) {" << std::endl;
} else
m_Impl << "\t" << "String ref = value;" << std::endl;
m_Impl << "\t" << "if (!ref.IsEmpty() && !utils.ValidateName(\"" << field.Type.TypeName << "\", ref))" << std::endl
<< "\t\t" << "BOOST_THROW_EXCEPTION(ValidationError(dynamic_cast<ConfigObject *>(this), boost::assign::list_of(\"" << field.Name << "\"), \"Object '\" + ref + \"' of type '" << field.Type.TypeName
<< "' does not exist.\"));" << std::endl;
if (field.Type.ArrayRank > 0) {
m_Impl << "\t\t" << "}" << std::endl
<< "\t" << "}" << std::endl;
}
}
}
@ -521,7 +542,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&)
/* SetField */
m_Header << "protected:" << std::endl
<< "\t" << "virtual void SetField(int id, const Value& value, bool suppress_events = false, const Value& cookie = Empty);" << std::endl;
<< "\t" << "virtual void SetField(int id, const Value& value, bool suppress_events = false, const Value& cookie = Empty) override;" << std::endl;
m_Impl << "void ObjectImpl<" << klass.Name << ">::SetField(int id, const Value& value, bool suppress_events, const Value& cookie)" << std::endl
<< "{" << std::endl;
@ -565,7 +586,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&)
/* GetField */
m_Header << "protected:" << std::endl
<< "\t" << "virtual Value GetField(int id) const;" << std::endl;
<< "\t" << "virtual Value GetField(int id) const override;" << std::endl;
m_Impl << "Value ObjectImpl<" << klass.Name << ">::GetField(int id) const" << std::endl
<< "{" << std::endl;
@ -598,7 +619,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&)
/* ValidateField */
m_Header << "protected:" << std::endl
<< "\t" << "virtual void ValidateField(int id, const Value& value, const ValidationUtils& utils);" << std::endl;
<< "\t" << "virtual void ValidateField(int id, const Value& value, const ValidationUtils& utils) override;" << std::endl;
m_Impl << "void ObjectImpl<" << klass.Name << ">::ValidateField(int id, const Value& value, const ValidationUtils& utils)" << std::endl
<< "{" << std::endl;
@ -642,7 +663,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&)
/* NotifyField */
m_Header << "protected:" << std::endl
<< "\t" << "virtual void NotifyField(int id, const Value& cookie = Empty);" << std::endl;
<< "\t" << "virtual void NotifyField(int id, const Value& cookie = Empty) override;" << std::endl;
m_Impl << "void ObjectImpl<" << klass.Name << ">::NotifyField(int id, const Value& cookie)" << std::endl
<< "{" << std::endl;
@ -723,17 +744,105 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&)
m_Impl << "void ObjectImpl<" << klass.Name << ">::Set" << it->GetFriendlyName() << "(" << it->Type.GetArgumentType() << " value, bool suppress_events, const Value& cookie)" << std::endl
<< "{" << std::endl;
if (it->Type.IsName || !it->TrackAccessor.empty())
m_Impl << "\t" << "Value oldValue = Get" << it->GetFriendlyName() << "();" << std::endl;
if (it->SetAccessor.empty() && !(it->Attributes & FANoStorage))
m_Impl << "\t" << "m_" << it->GetFriendlyName() << " = value;" << std::endl;
else
m_Impl << it->SetAccessor << std::endl << std::endl;
if (it->Type.IsName || !it->TrackAccessor.empty()) {
m_Impl << "\t" << "ConfigObject *dobj = dynamic_cast<ConfigObject *>(this);" << std::endl;
if (it->Name != "active") {
m_Impl << "\t" << "if (!dobj || dobj->IsActive())" << std::endl
<< "\t";
}
m_Impl << "\t" << "Track" << it->GetFriendlyName() << "(oldValue, value);" << std::endl;
}
m_Impl << "\t" << "if (!suppress_events)" << std::endl
<< "\t\t" << "Notify" << it->GetFriendlyName() << "(cookie);" << std::endl
<< "}" << std::endl << std::endl;
}
}
m_Header << "protected:" << std::endl;
bool needs_tracking = false;
/* tracking */
for (it = klass.Fields.begin(); it != klass.Fields.end(); it++) {
if (!it->Type.IsName && it->TrackAccessor.empty())
continue;
needs_tracking = true;
m_Header << "\t" << "virtual void Track" << it->GetFriendlyName() << "(" << it->Type.GetArgumentType() << " oldValue, " << it->Type.GetArgumentType() << " newValue);";
m_Impl << "void ObjectImpl<" << klass.Name << ">::Track" << it->GetFriendlyName() << "(" << it->Type.GetArgumentType() << " oldValue, " << it->Type.GetArgumentType() << " newValue)" << std::endl
<< "{" << std::endl;
if (!it->TrackAccessor.empty())
m_Impl << "\t" << it->TrackAccessor << std::endl;
if (it->Type.ArrayRank > 0) {
m_Impl << "\t" << "if (oldValue) {" << std::endl
<< "\t\t" << "ObjectLock olock(oldValue);" << std::endl
<< "\t\t" << "BOOST_FOREACH(const String& ref, oldValue) {" << std::endl
<< "\t\t\t" << "DependencyGraph::RemoveDependency(this, ConfigObject::GetObject(\"" << it->Type.TypeName << "\", ref).get());" << std::endl
<< "\t\t" << "}" << std::endl
<< "\t" << "}" << std::endl
<< "\t" << "if (newValue) {" << std::endl
<< "\t\t" << "ObjectLock olock(newValue);" << std::endl
<< "\t\t" << "BOOST_FOREACH(const String& ref, newValue) {" << std::endl
<< "\t\t\t" << "DependencyGraph::AddDependency(this, ConfigObject::GetObject(\"" << it->Type.TypeName << "\", ref).get());" << std::endl
<< "\t\t" << "}" << std::endl
<< "\t" << "}" << std::endl;
} else {
m_Impl << "\t" << "if (!oldValue.IsEmpty())" << std::endl
<< "\t\t" << "DependencyGraph::RemoveDependency(this, ConfigObject::GetObject(\"" << it->Type.TypeName << "\", oldValue).get());" << std::endl
<< "\t" << "if (!newValue.IsEmpty())" << std::endl
<< "\t\t" << "DependencyGraph::AddDependency(this, ConfigObject::GetObject(\"" << it->Type.TypeName << "\", newValue).get());" << std::endl;
}
m_Impl << "}" << std::endl << std::endl;
}
/* start/stop */
if (needs_tracking) {
m_Header << "virtual void Start(void) override;" << std::endl
<< "virtual void Stop(void) override;" << std::endl;
m_Impl << "void ObjectImpl<" << klass.Name << ">::Start(void)" << std::endl
<< "{" << std::endl
<< "\t" << klass.Parent << "::Start();" << std::endl << std::endl;
for (it = klass.Fields.begin(); it != klass.Fields.end(); it++) {
if (!(it->Type.IsName))
continue;
m_Impl << "\t" << "Track" << it->GetFriendlyName() << "(Empty, Get" << it->GetFriendlyName() << "());" << std::endl;
}
m_Impl << "}" << std::endl << std::endl
<< "void ObjectImpl<" << klass.Name << ">::Stop(void)" << std::endl
<< "{" << std::endl
<< "\t" << klass.Parent << "::Stop();" << std::endl << std::endl;
for (it = klass.Fields.begin(); it != klass.Fields.end(); it++) {
if (!(it->Type.IsName))
continue;
m_Impl << "\t" << "Track" << it->GetFriendlyName() << "(Get" << it->GetFriendlyName() << "(), Empty);" << std::endl;
}
m_Impl << "}" << std::endl << std::endl;
}
/* notify */
for (it = klass.Fields.begin(); it != klass.Fields.end(); it++) {
std::string prot;
@ -787,8 +896,10 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&)
m_Header << "private:" << std::endl;
for (it = klass.Fields.begin(); it != klass.Fields.end(); it++) {
if (!(it->Attributes & FANoStorage))
m_Header << "\t" << it->Type.GetRealType() << " m_" << it->GetFriendlyName() << ";" << std::endl;
if (it->Attributes & FANoStorage)
continue;
m_Header << "\t" << it->Type.GetRealType() << " m_" << it->GetFriendlyName() << ";" << std::endl;
}
/* signal */
@ -1178,6 +1289,7 @@ void ClassCompiler::CompileStream(const std::string& path, std::istream& input,
<< "#include \"base/objectlock.hpp\"" << std::endl
<< "#include \"base/utility.hpp\"" << std::endl
<< "#include \"base/convert.hpp\"" << std::endl
<< "#include \"base/dependencygraph.hpp\"" << std::endl
<< "#include <boost/foreach.hpp>" << std::endl
<< "#include <boost/assign/list_of.hpp>" << std::endl
<< "#ifdef _MSC_VER" << std::endl

View File

@ -42,7 +42,8 @@ enum FieldAccessorType
{
FTGet,
FTSet,
FTDefault
FTDefault,
FTTrack
};
struct FieldAccessor
@ -75,13 +76,21 @@ struct FieldType
{
bool IsName;
std::string TypeName;
int ArrayRank;
FieldType(void)
: IsName(false), ArrayRank(0)
{ }
inline std::string GetRealType(void) const
{
if (ArrayRank > 0)
return "Array::Ptr";
if (IsName)
return "String";
else
return TypeName;
return TypeName;
}
inline std::string GetArgumentType(void) const
@ -106,6 +115,7 @@ struct Field
std::string SetAccessor;
bool PureSetAccessor;
std::string DefaultAccessor;
std::string TrackAccessor;
Field(void)
: Attributes(0), PureGetAccessor(false), PureSetAccessor(false)
@ -203,6 +213,8 @@ public:
void HandleInclude(const std::string& path, const ClassDebugInfo& locp);
void HandleAngleInclude(const std::string& path, const ClassDebugInfo& locp);
void HandleImplInclude(const std::string& path, const ClassDebugInfo& locp);
void HandleAngleImplInclude(const std::string& path, const ClassDebugInfo& locp);
void HandleClass(const Klass& klass, const ClassDebugInfo& locp);
void HandleValidator(const Validator& validator, const ClassDebugInfo& locp);
void HandleNamespaceBegin(const std::string& name, const ClassDebugInfo& locp);