Implement support for unregistering objects

refs #9101
This commit is contained in:
Gunnar Beutner 2015-08-13 09:02:52 +02:00
parent f405a26052
commit e2290d5012
27 changed files with 363 additions and 80 deletions

View File

@ -177,6 +177,14 @@ void DynamicObject::Register(void)
dtype->RegisterObject(this);
}
void DynamicObject::Unregister(void)
{
ASSERT(!OwnsLock());
DynamicType::Ptr dtype = GetType();
dtype->UnregisterObject(this);
}
void DynamicObject::Start(void)
{
ASSERT(!OwnsLock());

View File

@ -58,6 +58,7 @@ public:
bool IsAttributeModified(const String& attr) const;
void Register(void);
void Unregister(void);
void Activate(void);
void Deactivate(void);

View File

@ -34,6 +34,7 @@ enum HAMode
class NameComposer {
public:
virtual String MakeName(const String& shortName, const Object::Ptr& context) const = 0;
virtual Dictionary::Ptr ParseName(const String& name) const = 0;
};
}}}

View File

@ -113,6 +113,18 @@ void DynamicType::RegisterObject(const DynamicObject::Ptr& object)
}
}
void DynamicType::UnregisterObject(const DynamicObject::Ptr& object)
{
String name = object->GetName();
{
ObjectLock olock(this);
m_ObjectMap.erase(name);
m_ObjectVector.erase(std::remove(m_ObjectVector.begin(), m_ObjectVector.end(), object), m_ObjectVector.end());
}
}
DynamicObject::Ptr DynamicType::GetObject(const String& name) const
{
ObjectLock olock(this);

View File

@ -46,6 +46,7 @@ public:
DynamicObject::Ptr GetObject(const String& name) const;
void RegisterObject(const DynamicObject::Ptr& object);
void UnregisterObject(const DynamicObject::Ptr& object);
static std::vector<DynamicType::Ptr> GetTypes(void);
std::pair<DynamicTypeIterator<DynamicObject>, DynamicTypeIterator<DynamicObject> > GetObjects(void);

View File

@ -56,6 +56,16 @@ Type::Ptr Type::GetByName(const String& name)
return ptype;
}
String Type::GetPluralName(void) const
{
String name = GetName();
if (name[name.GetLength() - 1] == 'y')
return name.SubStr(0, name.GetLength() - 1) + "ies";
else
return name + "s";
}
Object::Ptr Type::Instantiate(void) const
{
ObjectFactory factory = GetFactory();

View File

@ -80,6 +80,8 @@ public:
virtual Field GetFieldInfo(int id) const = 0;
virtual int GetFieldCount(void) const = 0;
String GetPluralName(void) const;
Object::Ptr Instantiate(void) const;
bool IsAssignableFrom(const Type::Ptr& other) const;

View File

@ -258,10 +258,14 @@ int DaemonCommand::Run(const po::variables_map& vm, const std::vector<std::strin
}
}
// activate config only after daemonization: it starts threads and that is not compatible with fork()
if (!ConfigItem::ActivateItems()) {
Log(LogCritical, "cli", "Error activating configuration.");
return EXIT_FAILURE;
{
WorkQueue upq(25000, Application::GetConcurrency());
// activate config only after daemonization: it starts threads and that is not compatible with fork()
if (!ConfigItem::ActivateItems(upq, true)) {
Log(LogCritical, "cli", "Error activating configuration.");
return EXIT_FAILURE;
}
}
if (vm.count("daemonize")) {

View File

@ -123,7 +123,8 @@ bool DaemonUtility::LoadConfigFiles(const std::vector<std::string>& configs,
if (!DaemonUtility::ValidateConfigFiles(configs, objectsFile))
return false;
bool result = ConfigItem::CommitItems();
WorkQueue upq(25000, Application::GetConcurrency());
bool result = ConfigItem::CommitItems(upq);
if (!result)
return false;

View File

@ -268,6 +268,19 @@ void ConfigItem::Register(void)
}
}
/**
* Unregisters the configuration item.
*/
void ConfigItem::Unregister(void)
{
if (m_Object)
m_Object->Unregister();
boost::mutex::scoped_lock lock(m_Mutex);
m_UnnamedItems.erase(std::remove(m_UnnamedItems.begin(), m_UnnamedItems.end(), this), m_UnnamedItems.end());
m_Items[m_Type].erase(m_Name);
}
/**
* Retrieves a configuration item by type and name.
*
@ -292,7 +305,7 @@ ConfigItem::Ptr ConfigItem::GetObject(const String& type, const String& name)
return it2->second;
}
bool ConfigItem::CommitNewItems(WorkQueue& upq)
bool ConfigItem::CommitNewItems(WorkQueue& upq, std::vector<ConfigItem::Ptr>& newItems)
{
typedef std::pair<ConfigItem::Ptr, bool> ItemPair;
std::vector<ItemPair> items;
@ -320,6 +333,7 @@ bool ConfigItem::CommitNewItems(WorkQueue& upq)
return true;
BOOST_FOREACH(const ItemPair& ip, items) {
newItems.push_back(ip.first);
upq.Enqueue(boost::bind(&ConfigItem::Commit, ip.first, ip.second));
}
@ -398,7 +412,7 @@ bool ConfigItem::CommitNewItems(WorkQueue& upq)
if (upq.HasExceptions())
return false;
if (!CommitNewItems(upq))
if (!CommitNewItems(upq, newItems))
return false;
}
}
@ -406,44 +420,53 @@ bool ConfigItem::CommitNewItems(WorkQueue& upq)
return true;
}
bool ConfigItem::CommitItems(void)
bool ConfigItem::CommitItems(WorkQueue& upq)
{
WorkQueue upq(25000, Application::GetConcurrency());
Log(LogInformation, "ConfigItem", "Committing config items");
if (!CommitNewItems(upq)) {
std::vector<ConfigItem::Ptr> newItems;
if (!CommitNewItems(upq, newItems)) {
upq.ReportExceptions("config");
BOOST_FOREACH(const ConfigItem::Ptr& item, newItems) {
item->Unregister();
}
return false;
}
ApplyRule::CheckMatches();
/* log stats for external parsers */
BOOST_FOREACH(const DynamicType::Ptr& type, DynamicType::GetTypes()) {
int count = std::distance(type->GetObjects().first, type->GetObjects().second);
if (count > 0)
Log(LogInformation, "ConfigItem")
<< "Checked " << count << " " << type->GetName() << "(s).";
typedef std::map<Type::Ptr, int> ItemCountMap;
ItemCountMap itemCounts;
BOOST_FOREACH(const ConfigItem::Ptr& item, newItems) {
itemCounts[item->m_Object->GetReflectionType()]++;
}
BOOST_FOREACH(const ItemCountMap::value_type& kv, itemCounts) {
Log(LogInformation, "ConfigItem")
<< "Instantiated " << kv.second << " " << kv.first->GetPluralName() << ".";
}
return true;
}
bool ConfigItem::ActivateItems(void)
bool ConfigItem::ActivateItems(WorkQueue& upq, bool restoreState)
{
/* restore the previous program state */
try {
DynamicObject::RestoreObjects(Application::GetStatePath());
} catch (const std::exception& ex) {
Log(LogCritical, "ConfigItem")
<< "Failed to restore state file: " << DiagnosticInformation(ex);
if (restoreState) {
/* restore the previous program state */
try {
DynamicObject::RestoreObjects(Application::GetStatePath());
} catch (const std::exception& ex) {
Log(LogCritical, "ConfigItem")
<< "Failed to restore state file: " << DiagnosticInformation(ex);
}
}
Log(LogInformation, "ConfigItem", "Triggering Start signal for config items");
WorkQueue upq(25000, Application::GetConcurrency());
BOOST_FOREACH(const DynamicType::Ptr& type, DynamicType::GetTypes()) {
BOOST_FOREACH(const DynamicObject::Ptr& object, type->GetObjects()) {
if (object->IsActive())
@ -481,40 +504,11 @@ bool ConfigItem::CommitAndActivate(void)
{
WorkQueue upq(25000, Application::GetConcurrency());
if (!CommitNewItems(upq)) {
upq.ReportExceptions("ConfigItem");
boost::mutex::scoped_lock lock(m_Mutex);
m_Items.clear();
m_UnnamedItems.clear();
if (!CommitItems(upq))
return false;
}
BOOST_FOREACH(const DynamicType::Ptr& type, DynamicType::GetTypes()) {
BOOST_FOREACH(const DynamicObject::Ptr& object, type->GetObjects()) {
if (object->IsActive())
continue;
#ifdef I2_DEBUG
Log(LogDebug, "ConfigItem")
<< "Activating object '" << object->GetName() << "' of type '" << object->GetType()->GetName() << "'";
#endif /* I2_DEBUG */
upq.Enqueue(boost::bind(&DynamicObject::Activate, object));
}
}
upq.Join();
if (upq.HasExceptions()) {
upq.ReportExceptions("ConfigItem");
boost::mutex::scoped_lock lock(m_Mutex);
m_Items.clear();
m_UnnamedItems.clear();
if (!ActivateItems(upq, false))
return false;
}
return true;
}

View File

@ -55,6 +55,7 @@ public:
DynamicObject::Ptr Commit(bool discard = true);
void Register(void);
void Unregister(void);
DebugInfo GetDebugInfo(void) const;
Dictionary::Ptr GetScope(void) const;
@ -64,8 +65,8 @@ public:
static ConfigItem::Ptr GetObject(const String& type,
const String& name);
static bool CommitItems(void);
static bool ActivateItems(void);
static bool CommitItems(WorkQueue& upq);
static bool ActivateItems(WorkQueue& upq, bool restoreState);
static bool CommitAndActivate(void);
@ -97,7 +98,7 @@ private:
static ConfigItem::Ptr GetObjectUnlocked(const String& type,
const String& name);
static bool CommitNewItems(WorkQueue& upq);
static bool CommitNewItems(WorkQueue& upq, std::vector<ConfigItem::Ptr>& newItems);
};
}

View File

@ -23,6 +23,8 @@
#include "base/logger.hpp"
#include "base/exception.hpp"
#include <boost/foreach.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
using namespace icinga;
@ -45,6 +47,27 @@ String DependencyNameComposer::MakeName(const String& shortName, const Object::P
return name;
}
Dictionary::Ptr DependencyNameComposer::ParseName(const String& name) const
{
std::vector<String> tokens;
boost::algorithm::split(tokens, name, boost::is_any_of("!"));
if (tokens.size() < 2)
BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid Dependency name."));
Dictionary::Ptr result = new Dictionary();
result->Set("child_host_name", tokens[0]);
if (tokens.size() > 2) {
result->Set("child_service_name", tokens[1]);
result->Set("name", tokens[2]);
} else {
result->Set("name", tokens[1]);
}
return result;
}
void Dependency::OnConfigLoaded(void)
{
Value defaultFilter;

View File

@ -30,6 +30,7 @@ class I2_ICINGA_API DependencyNameComposer : public NameComposer
{
public:
virtual String MakeName(const String& shortName, const Object::Ptr& context) const;
virtual Dictionary::Ptr ParseName(const String& name) const;
};
}}}

View File

@ -29,6 +29,8 @@
#include "base/initialize.hpp"
#include "base/scriptglobal.hpp"
#include <boost/foreach.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
using namespace icinga;
@ -54,6 +56,27 @@ String NotificationNameComposer::MakeName(const String& shortName, const Object:
return name;
}
Dictionary::Ptr NotificationNameComposer::ParseName(const String& name) const
{
std::vector<String> tokens;
boost::algorithm::split(tokens, name, boost::is_any_of("!"));
if (tokens.size() < 2)
BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid Notification name."));
Dictionary::Ptr result = new Dictionary();
result->Set("host_name", tokens[0]);
if (tokens.size() > 2) {
result->Set("service_name", tokens[1]);
result->Set("name", tokens[2]);
} else {
result->Set("name", tokens[1]);
}
return result;
}
void Notification::StaticInitialize(void)
{
ScriptGlobal::Set("OK", StateFilterOK);

View File

@ -29,6 +29,7 @@ class I2_ICINGA_API NotificationNameComposer : public NameComposer
{
public:
virtual String MakeName(const String& shortName, const Object::Ptr& context) const;
virtual Dictionary::Ptr ParseName(const String& name) const;
};
}}}

View File

@ -31,6 +31,8 @@
#include "base/logger.hpp"
#include "base/exception.hpp"
#include <boost/foreach.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
using namespace icinga;
@ -57,6 +59,27 @@ String ScheduledDowntimeNameComposer::MakeName(const String& shortName, const Ob
return name;
}
Dictionary::Ptr ScheduledDowntimeNameComposer::ParseName(const String& name) const
{
std::vector<String> tokens;
boost::algorithm::split(tokens, name, boost::is_any_of("!"));
if (tokens.size() < 2)
BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid ScheduledDowntime name."));
Dictionary::Ptr result = new Dictionary();
result->Set("host_name", tokens[0]);
if (tokens.size() > 2) {
result->Set("service_name", tokens[1]);
result->Set("name", tokens[2]);
} else {
result->Set("name", tokens[1]);
}
return result;
}
void ScheduledDowntime::StaticInitialize(void)
{
l_Timer = new Timer();

View File

@ -29,6 +29,7 @@ class I2_ICINGA_API ScheduledDowntimeNameComposer : public NameComposer
{
public:
virtual String MakeName(const String& shortName, const Object::Ptr& context) const;
virtual Dictionary::Ptr ParseName(const String& name) const;
};
}}}

View File

@ -27,6 +27,8 @@
#include "base/utility.hpp"
#include <boost/foreach.hpp>
#include <boost/bind/apply.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
using namespace icinga;
@ -42,6 +44,21 @@ String ServiceNameComposer::MakeName(const String& shortName, const Object::Ptr&
return service->GetHostName() + "!" + shortName;
}
Dictionary::Ptr ServiceNameComposer::ParseName(const String& name) const
{
std::vector<String> tokens;
boost::algorithm::split(tokens, name, boost::is_any_of("!"));
if (tokens.size() < 2)
BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid Service name."));
Dictionary::Ptr result = new Dictionary();
result->Set("host_name", tokens[0]);
result->Set("name", tokens[1]);
return result;
}
void Service::OnAllConfigLoaded(void)
{
Checkable::OnAllConfigLoaded();

View File

@ -32,6 +32,7 @@ class I2_ICINGA_API ServiceNameComposer : public NameComposer
{
public:
virtual String MakeName(const String& shortName, const Object::Ptr& context) const;
virtual Dictionary::Ptr ParseName(const String& name) const;
};
}}}

View File

@ -23,8 +23,8 @@ mkclass_target(zone.ti zone.tcpp zone.thpp)
set(remote_SOURCES
actionshandler.cpp apiaction.cpp
apifunction.cpp apilistener.cpp apilistener.thpp apilistener-sync.cpp
apiuser.cpp apiuser.thpp authority.cpp base64.cpp createobjecthandler.cpp configfileshandler.cpp
configmoduleshandler.cpp configmoduleutility.cpp configstageshandler.cpp
apiuser.cpp apiuser.thpp authority.cpp base64.cpp createobjecthandler.cpp deleteobjecthandler.cpp
configfileshandler.cpp configmoduleshandler.cpp configmoduleutility.cpp configstageshandler.cpp
endpoint.cpp endpoint.thpp filterutility.cpp
httpchunkedencoding.cpp httpconnection.cpp httphandler.cpp httprequest.cpp httpresponse.cpp
httputility.cpp jsonrpc.cpp jsonrpcconnection.cpp jsonrpcconnection-heartbeat.cpp

View File

@ -47,6 +47,14 @@ bool CreateObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r
String name = request.RequestUrl->GetPath()[2];
NameComposer *nc = dynamic_cast<NameComposer *>(type.get());
Dictionary::Ptr nameParts;
if (nc) {
nameParts = nc->ParseName(name);
name = nameParts->Get("name");
}
Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request);
ConfigItemBuilder::Ptr builder = new ConfigItemBuilder();
@ -64,6 +72,14 @@ bool CreateObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r
}
}
if (nameParts) {
ObjectLock olock(nameParts);
BOOST_FOREACH(const Dictionary::Pair& kv, nameParts) {
SetExpression *expr = new SetExpression(MakeIndexer(ScopeThis, kv.first), OpSetLiteral, MakeLiteral(kv.second));
builder->AddExpression(expr);
}
}
Dictionary::Ptr attrs = params->Get("attrs");
if (attrs) {
@ -77,11 +93,28 @@ bool CreateObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r
ConfigItem::Ptr item = builder->Compile();
item->Register();
ConfigItem::CommitAndActivate();
WorkQueue upq;
Dictionary::Ptr result1 = new Dictionary();
result1->Set("code", 200);
result1->Set("status", "Object created.");
int code;
String status;
if (!ConfigItem::CommitItems(upq) || !ConfigItem::ActivateItems(upq, false)) {
code = 500;
status = "Object could not be created.";
Array::Ptr errors = new Array();
BOOST_FOREACH(const boost::exception_ptr& ex, upq.GetExceptions()) {
errors->Add(DiagnosticInformation(ex));
}
result1->Set("errors", errors);
} else {
code = 200;
status = "Object created";
}
result1->Set("code", code);
result1->Set("status", status);
Array::Ptr results = new Array();
results->Add(result1);
@ -89,7 +122,7 @@ bool CreateObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r
Dictionary::Ptr result = new Dictionary();
result->Set("results", results);
response.SetStatus(200, "OK");
response.SetStatus(code, status);
HttpUtility::SendJsonBody(response, result);
return true;

View File

@ -0,0 +1,97 @@
/******************************************************************************
* 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/deleteobjecthandler.hpp"
#include "remote/httputility.hpp"
#include "remote/filterutility.hpp"
#include "remote/apiaction.hpp"
#include "config/configitem.hpp"
#include "base/exception.hpp"
#include "base/serializer.hpp"
#include <boost/algorithm/string.hpp>
#include <set>
using namespace icinga;
REGISTER_URLHANDLER("/v1", DeleteObjectHandler);
bool DeleteObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response)
{
if (request.RequestMethod != "DELETE")
return false;
if (request.RequestUrl->GetPath().size() < 2)
return false;
Type::Ptr type = FilterUtility::TypeFromPluralName(request.RequestUrl->GetPath()[1]);
if (!type)
return false;
QueryDescription qd;
qd.Types.insert(type);
Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request);
params->Set("type", type->GetName());
if (request.RequestUrl->GetPath().size() >= 3) {
String attr = type->GetName();
boost::algorithm::to_lower(attr);
params->Set(attr, request.RequestUrl->GetPath()[2]);
}
std::vector<DynamicObject::Ptr> objs = FilterUtility::GetFilterTargets(qd, params);
Array::Ptr results = new Array();
BOOST_FOREACH(const DynamicObject::Ptr& obj, objs) {
Dictionary::Ptr result1 = new Dictionary();
result1->Set("type", type->GetName());
result1->Set("name", obj->GetName());
ConfigItem::Ptr item = ConfigItem::GetObject(type->GetName(), obj->GetName());
try {
obj->Deactivate();
if (item)
item->Unregister();
else
obj->Unregister();
result1->Set("code", 200);
result1->Set("status", "Object was deleted.");
} catch (const std::exception& ex) {
result1->Set("code", 500);
result1->Set("status", "Object could not be deleted: " + DiagnosticInformation(ex));
}
results->Add(result1);
}
Dictionary::Ptr result = new Dictionary();
result->Set("results", results);
response.SetStatus(200, "OK");
HttpUtility::SendJsonBody(response, result);
return true;
}

View File

@ -0,0 +1,38 @@
/******************************************************************************
* 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 DELETEOBJECTHANDLER_H
#define DELETEOBJECTHANDLER_H
#include "remote/httphandler.hpp"
namespace icinga
{
class I2_REMOTE_API DeleteObjectHandler : public HttpHandler
{
public:
DECLARE_PTR_TYPEDEFS(DeleteObjectHandler);
virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response);
};
}
#endif /* DELETEOBJECTHANDLER_H */

View File

@ -38,7 +38,7 @@ Type::Ptr FilterUtility::TypeFromPluralName(const String& pluralName)
Type::Ptr type = Type::GetByName(dtype->GetName());
ASSERT(type);
String pname = GetPluralName(type);
String pname = type->GetPluralName();
boost::algorithm::to_lower(pname);
if (uname == pname)
@ -48,16 +48,6 @@ Type::Ptr FilterUtility::TypeFromPluralName(const String& pluralName)
return Type::Ptr();
}
String FilterUtility::GetPluralName(const Type::Ptr& type)
{
String name = type->GetName();
if (name[name.GetLength() - 1] == 'y')
return name.SubStr(0, name.GetLength() - 1) + "ies";
else
return name + "s";
}
DynamicObject::Ptr FilterUtility::GetObjectByTypeAndName(const String& type, const String& name)
{
DynamicType::Ptr dtype = DynamicType::GetByName(type);
@ -82,7 +72,7 @@ std::vector<DynamicObject::Ptr> FilterUtility::GetFilterTargets(const QueryDescr
result.push_back(obj);
}
attr = GetPluralName(type);
attr = type->GetPluralName();
boost::algorithm::to_lower(attr);
if (query->Contains(attr)) {

View File

@ -41,7 +41,6 @@ struct QueryDescription
class I2_REMOTE_API FilterUtility
{
public:
static String GetPluralName(const Type::Ptr& type);
static Type::Ptr TypeFromPluralName(const String& pluralName);
static DynamicObject::Ptr GetObjectByTypeAndName(const String& type, const String& name);
static std::vector<DynamicObject::Ptr> GetFilterTargets(const QueryDescription& qd, const Dictionary::Ptr& query);

View File

@ -65,7 +65,7 @@ bool ModifyObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r
BOOST_FOREACH(const DynamicObject::Ptr& obj, objs) {
Dictionary::Ptr result1 = new Dictionary();
result1->Set("type", obj->GetReflectionType()->GetName());
result1->Set("type", type->GetName());
result1->Set("name", obj->GetName());
String key;

View File

@ -87,7 +87,8 @@ struct GlobalConfigFixture {
DaemonUtility::LoadConfigFiles(configs, "icinga2.debug", "icinga2.vars");
/* ignore config errors */
ConfigItem::ActivateItems();
WorkQueue upq;
ConfigItem::ActivateItems(upq, false);
}
~GlobalConfigFixture()