Refactor the WorkQueue class to make error reporting easier

refs #7709
This commit is contained in:
Gunnar Beutner 2014-12-18 15:11:57 +01:00
parent c3cf7682b9
commit 873e294158
38 changed files with 277 additions and 466 deletions

View File

@ -205,8 +205,9 @@ int Main(void)
String initconfig = Application::GetSysconfDir() + "/icinga2/init.conf";
if (Utility::PathExists(initconfig)) {
ConfigCompilerContext::GetInstance()->Reset();
ConfigCompiler::CompileFile(initconfig);
ScriptFrame frame;
Expression *expression = ConfigCompiler::CompileFile(initconfig);
expression->Evaluate(frame);
}
#ifndef _WIN32

View File

@ -27,7 +27,7 @@ set(base_SOURCES
convert.cpp debuginfo.cpp dictionary.cpp dictionary-script.cpp dynamicobject.cpp dynamicobject.thpp dynamictype.cpp
exception.cpp fifo.cpp filelogger.cpp filelogger.thpp initialize.cpp json.cpp logger.cpp logger.thpp
netstring.cpp networkstream.cpp number.cpp number-script.cpp object.cpp object-script.cpp primitivetype.cpp process.cpp
ringbuffer.cpp scripterror.cpp scriptframe.cpp scriptfunction.cpp scriptfunctionwrapper.cpp scriptglobal.cpp
ringbuffer.cpp scriptframe.cpp scriptfunction.cpp scriptfunctionwrapper.cpp scriptglobal.cpp
scriptutils.cpp serializer.cpp socket.cpp stacktrace.cpp
statsfunction.cpp stdiostream.cpp stream.cpp streamlogger.cpp streamlogger.thpp string.cpp string-script.cpp
sysloglogger.cpp sysloglogger.thpp tcpsocket.cpp thinmutex.cpp threadpool.cpp timer.cpp

View File

@ -31,6 +31,7 @@
#include "base/initialize.hpp"
#include "base/workqueue.hpp"
#include "base/context.hpp"
#include "base/application.hpp"
#include <fstream>
#include <boost/foreach.hpp>
#include <boost/exception/errinfo_api_function.hpp>
@ -310,7 +311,7 @@ void DynamicObject::RestoreObjects(const String& filename, int attributeTypes)
unsigned long restored = 0;
ParallelWorkQueue upq;
WorkQueue upq(25000, Application::GetConcurrency());
String message;
while (NetString::ReadStringFromStream(sfp, &message)) {

View File

@ -22,7 +22,7 @@
#include "base/debug.hpp"
#include "base/objectlock.hpp"
#include "base/convert.hpp"
#include "base/scripterror.hpp"
#include "base/exception.hpp"
using namespace icinga;

View File

@ -153,3 +153,24 @@ String icinga::DiagnosticInformation(boost::exception_ptr eptr)
return boost::diagnostic_information(eptr);
}
ScriptError::ScriptError(const String& message)
: m_Message(message)
{ }
ScriptError::ScriptError(const String& message, const DebugInfo& di)
: m_Message(message), m_DebugInfo(di)
{ }
ScriptError::~ScriptError(void) throw()
{ }
const char *ScriptError::what(void) const throw()
{
return m_Message.CStr();
}
DebugInfo ScriptError::GetDebugInfo(void) const
{
return m_DebugInfo;
}

View File

@ -25,6 +25,7 @@
#include "base/stacktrace.hpp"
#include "base/context.hpp"
#include "base/utility.hpp"
#include "base/debuginfo.hpp"
#include <sstream>
#include <boost/exception/errinfo_api_function.hpp>
#include <boost/exception/errinfo_errno.hpp>
@ -42,6 +43,25 @@ namespace icinga
class I2_BASE_API user_error : virtual public std::exception, virtual public boost::exception
{ };
/*
* @ingroup base
*/
class I2_BASE_API ScriptError : virtual public user_error
{
public:
ScriptError(const String& message);
ScriptError(const String& message, const DebugInfo& di);
~ScriptError(void) throw();
virtual const char *what(void) const throw();
DebugInfo GetDebugInfo(void) const;
private:
String m_Message;
DebugInfo m_DebugInfo;
};
I2_BASE_API StackTrace *GetLastExceptionStack(void);
I2_BASE_API void SetLastExceptionStack(const StackTrace& trace);
@ -58,9 +78,23 @@ String DiagnosticInformation(const T& ex, StackTrace *stack = NULL, ContextTrace
{
std::ostringstream result;
result << boost::diagnostic_information(ex);
const user_error *uex = dynamic_cast<const user_error *>(&ex);
if (dynamic_cast<const user_error *>(&ex) == NULL) {
String message = ex.what();
if (!uex || message.IsEmpty())
result << boost::diagnostic_information(ex);
else
result << "Error: " << message;
const ScriptError *dex = dynamic_cast<const ScriptError *>(&ex);
if (dex && !dex->GetDebugInfo().Path.IsEmpty()) {
result << "\nLocation:\n";
ShowCodeFragment(result, dex->GetDebugInfo());
}
if (!uex) {
if (boost::get_error_info<StackTraceErrorInfo>(ex) == NULL) {
result << std::endl;

View File

@ -1,45 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2014 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/scripterror.hpp"
#include <sstream>
using namespace icinga;
ScriptError::ScriptError(const String& message)
: m_Message(message)
{ }
ScriptError::ScriptError(const String& message, const DebugInfo& di)
: m_Message(message), m_DebugInfo(di)
{ }
ScriptError::~ScriptError(void) throw()
{ }
const char *ScriptError::what(void) const throw()
{
return m_Message.CStr();
}
DebugInfo ScriptError::GetDebugInfo(void) const
{
return m_DebugInfo;
}

View File

@ -1,51 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2014 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 SCRIPTERROR_H
#define SCRIPTERROR_H
#include "base/i2-base.hpp"
#include "base/debuginfo.hpp"
#include "base/exception.hpp"
namespace icinga
{
/*
* @ingroup base
*/
class I2_BASE_API ScriptError : virtual public user_error
{
public:
ScriptError(const String& message);
ScriptError(const String& message, const DebugInfo& di);
~ScriptError(void) throw();
virtual const char *what(void) const throw();
DebugInfo GetDebugInfo(void) const;
private:
String m_Message;
DebugInfo m_DebugInfo;
};
}
#endif /* SCRIPTERROR_H */

View File

@ -26,7 +26,7 @@
#include "base/objectlock.hpp"
#include "base/dynamictype.hpp"
#include "base/application.hpp"
#include "base/scripterror.hpp"
#include "base/exception.hpp"
#include <boost/foreach.hpp>
#include <boost/regex.hpp>
#include <algorithm>

View File

@ -22,16 +22,19 @@
#include "base/logger.hpp"
#include "base/convert.hpp"
#include "base/application.hpp"
#include "base/exception.hpp"
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
#include <boost/thread/tss.hpp>
using namespace icinga;
int WorkQueue::m_NextID = 1;
boost::thread_specific_ptr<WorkQueue *> l_ThreadWorkQueue;
WorkQueue::WorkQueue(size_t maxItems)
: m_ID(m_NextID++), m_MaxItems(maxItems), m_Stopped(false),
m_Processing(false), m_ExceptionCallback(WorkQueue::DefaultExceptionCallback)
WorkQueue::WorkQueue(size_t maxItems, int threadCount)
: m_ID(m_NextID++), m_MaxItems(maxItems), m_ThreadCount(threadCount), m_Spawned(false), m_Stopped(false),
m_Processing(0)
{
m_StatusTimer = new Timer();
m_StatusTimer->SetInterval(10);
@ -45,46 +48,53 @@ WorkQueue::~WorkQueue(void)
}
/**
* Enqueues a work item. Work items are guaranteed to be executed in the order
* they were enqueued in except when allowInterleaved is true in which case
* the new work item might be run immediately if it's being enqueued from
* within the WorkQueue thread.
* Enqueues a task. Tasks are guaranteed to be executed in the order
* they were enqueued in except if there is more than one worker thread or when
* allowInterleaved is true in which case the new task might be run
* immediately if it's being enqueued from within the WorkQueue thread.
*/
void WorkQueue::Enqueue(const WorkCallback& callback, bool allowInterleaved)
void WorkQueue::Enqueue(const Task& task, bool allowInterleaved)
{
bool wq_thread = (boost::this_thread::get_id() == GetThreadId());
bool wq_thread = IsWorkerThread();
if (wq_thread && allowInterleaved) {
callback();
task();
return;
}
WorkItem item;
item.Callback = callback;
item.AllowInterleaved = allowInterleaved;
boost::mutex::scoped_lock lock(m_Mutex);
if (m_Thread.get_id() == boost::thread::id())
m_Thread = boost::thread(boost::bind(&WorkQueue::WorkerThreadProc, this));
if (!m_Spawned) {
for (int i = 0; i < m_ThreadCount; i++) {
m_Threads.create_thread(boost::bind(&WorkQueue::WorkerThreadProc, this));
}
m_Spawned = true;
}
if (!wq_thread) {
while (m_Items.size() >= m_MaxItems)
while (m_Tasks.size() >= m_MaxItems)
m_CVFull.wait(lock);
}
m_Items.push_back(item);
m_Tasks.push_back(task);
if (m_Items.size() == 1)
if (m_Tasks.size() == 1)
m_CVEmpty.notify_all();
}
/**
* Waits until all currently enqueued tasks have completed. This only works reliably
* when no other thread is enqueuing new tasks when this method is called.
*
* @param stop Whether to stop the worker threads
*/
void WorkQueue::Join(bool stop)
{
boost::mutex::scoped_lock lock(m_Mutex);
while (m_Processing || !m_Items.empty())
while (m_Processing || !m_Tasks.empty())
m_CVStarved.wait(lock);
if (stop) {
@ -92,33 +102,73 @@ void WorkQueue::Join(bool stop)
m_CVEmpty.notify_all();
lock.unlock();
if (m_Thread.joinable())
m_Thread.join();
m_Threads.join_all();
m_Spawned = false;
}
}
boost::thread::id WorkQueue::GetThreadId(void) const
/**
* Checks whether the calling thread is one of the worker threads
* for this work queue.
*
* @returns true if called from one of the worker threads, false otherwise
*/
bool WorkQueue::IsWorkerThread(void) const
{
return m_Thread.get_id();
WorkQueue **pwq = l_ThreadWorkQueue.get();
if (!pwq)
return false;
return *pwq == this;
}
void WorkQueue::SetExceptionCallback(const ExceptionCallback& callback)
{
m_ExceptionCallback = callback;
}
/**
* Checks whether any exceptions have occurred while executing tasks for this
* work queue. When a custom exception callback is set this method will always
* return false.
*/
bool WorkQueue::HasExceptions(void) const
{
boost::mutex::scoped_lock lock(m_Mutex);
m_ExceptionCallback = callback;
return !m_Exceptions.empty();
}
/**
* Returns all exceptions which have occurred for tasks in this work queue. When a
* custom exception callback is set this method will always return an empty list.
*/
std::vector<boost::exception_ptr> WorkQueue::GetExceptions(void) const
{
boost::mutex::scoped_lock lock(m_Mutex);
return m_Exceptions;
}
void WorkQueue::ReportExceptions(const String& facility) const
{
std::vector<boost::exception_ptr> exceptions = GetExceptions();
BOOST_FOREACH(const boost::exception_ptr& eptr, exceptions) {
Log(LogCritical, facility)
<< DiagnosticInformation(eptr);
}
Log(LogCritical, facility)
<< exceptions.size() << " error" << (exceptions.size() != 1 ? "s" : "");
}
size_t WorkQueue::GetLength(void)
{
boost::mutex::scoped_lock lock(m_Mutex);
return m_Items.size();
}
void WorkQueue::DefaultExceptionCallback(boost::exception_ptr)
{
throw;
return m_Tasks.size();
}
void WorkQueue::StatusTimerHandler(void)
@ -126,7 +176,7 @@ void WorkQueue::StatusTimerHandler(void)
boost::mutex::scoped_lock lock(m_Mutex);
Log(LogNotice, "WorkQueue")
<< "#" << m_ID << " items: " << m_Items.size();
<< "#" << m_ID << " tasks: " << m_Tasks.size();
}
void WorkQueue::WorkerThreadProc(void)
@ -135,67 +185,47 @@ void WorkQueue::WorkerThreadProc(void)
idbuf << "WQ #" << m_ID;
Utility::SetThreadName(idbuf.str());
l_ThreadWorkQueue.reset(new WorkQueue *(this));
boost::mutex::scoped_lock lock(m_Mutex);
for (;;) {
while (m_Items.empty() && !m_Stopped)
while (m_Tasks.empty() && !m_Stopped)
m_CVEmpty.wait(lock);
if (m_Stopped)
break;
std::deque<WorkItem> items;
m_Items.swap(items);
if (items.size() >= m_MaxItems)
if (m_Tasks.size() >= m_MaxItems)
m_CVFull.notify_all();
m_Processing = true;
Task task = m_Tasks.front();
m_Tasks.pop_front();
m_Processing++;
lock.unlock();
BOOST_FOREACH(WorkItem& wi, items) {
try {
wi.Callback();
}
catch (const std::exception&) {
task();
} catch (const std::exception&) {
lock.lock();
ExceptionCallback callback = m_ExceptionCallback;
if (!m_ExceptionCallback)
m_Exceptions.push_back(boost::current_exception());
lock.unlock();
callback(boost::current_exception());
}
if (m_ExceptionCallback)
m_ExceptionCallback(boost::current_exception());
}
lock.lock();
m_Processing = false;
m_Processing--;
if (m_Tasks.empty())
m_CVStarved.notify_all();
}
}
ParallelWorkQueue::ParallelWorkQueue(void)
: m_QueueCount(Application::GetConcurrency()),
m_Queues(new WorkQueue[m_QueueCount]),
m_Index(0)
{ }
ParallelWorkQueue::~ParallelWorkQueue(void)
{
delete[] m_Queues;
}
void ParallelWorkQueue::Enqueue(const boost::function<void(void)>& callback)
{
m_Index++;
m_Queues[m_Index % m_QueueCount].Enqueue(callback);
}
void ParallelWorkQueue::Join(void)
{
for (unsigned int i = 0; i < m_QueueCount; i++)
m_Queues[i].Join();
}

View File

@ -32,13 +32,7 @@
namespace icinga
{
typedef boost::function<void (void)> WorkCallback;
struct WorkItem
{
WorkCallback Callback;
bool AllowInterleaved;
};
typedef boost::function<void (void)> Task;
/**
* A workqueue.
@ -50,53 +44,43 @@ class I2_BASE_API WorkQueue
public:
typedef boost::function<void (boost::exception_ptr)> ExceptionCallback;
WorkQueue(size_t maxItems = 25000);
WorkQueue(size_t maxItems = 25000, int threadCount = 1);
~WorkQueue(void);
void Enqueue(const WorkCallback& callback, bool allowInterleaved = false);
void Enqueue(const Task& task, bool allowInterleaved = false);
void Join(bool stop = false);
boost::thread::id GetThreadId(void) const;
bool IsWorkerThread(void) const;
size_t GetLength(void);
void SetExceptionCallback(const ExceptionCallback& callback);
size_t GetLength(void);
bool HasExceptions(void) const;
std::vector<boost::exception_ptr> GetExceptions(void) const;
void ReportExceptions(const String& facility) const;
private:
int m_ID;
static int m_NextID;
int m_ThreadCount;
bool m_Spawned;
boost::mutex m_Mutex;
mutable boost::mutex m_Mutex;
boost::condition_variable m_CVEmpty;
boost::condition_variable m_CVFull;
boost::condition_variable m_CVStarved;
boost::thread m_Thread;
boost::thread_group m_Threads;
size_t m_MaxItems;
bool m_Stopped;
bool m_Processing;
std::deque<WorkItem> m_Items;
int m_Processing;
std::deque<Task> m_Tasks;
ExceptionCallback m_ExceptionCallback;
std::vector<boost::exception_ptr> m_Exceptions;
Timer::Ptr m_StatusTimer;
void WorkerThreadProc(void);
void StatusTimerHandler(void);
static void DefaultExceptionCallback(boost::exception_ptr exp);
};
class I2_BASE_API ParallelWorkQueue
{
public:
ParallelWorkQueue(void);
~ParallelWorkQueue(void);
void Enqueue(const boost::function<void(void)>& callback);
void Join(void);
private:
unsigned int m_QueueCount;
WorkQueue *m_Queues;
unsigned int m_Index;
};
}

View File

@ -18,8 +18,8 @@
******************************************************************************/
#include "cli/daemoncommand.hpp"
#include "config/configcompilercontext.hpp"
#include "config/configcompiler.hpp"
#include "config/configcompilercontext.hpp"
#include "config/configitembuilder.hpp"
#include "base/logger.hpp"
#include "base/application.hpp"
@ -60,16 +60,20 @@ static String LoadAppType(const String& typeSpec)
return typeSpec.SubStr(index + 1);
}
static void ExecuteExpression(Expression *expression)
static bool ExecuteExpression(Expression *expression)
{
if (!expression)
return false;
try {
ScriptFrame frame;
expression->Evaluate(frame);
} catch (const ScriptError& ex) {
ConfigCompilerContext::GetInstance()->AddMessage(true, ex.what(), ex.GetDebugInfo());
} catch (const std::exception& ex) {
ConfigCompilerContext::GetInstance()->AddMessage(true, DiagnosticInformation(ex));
Log(LogCritical, "config", DiagnosticInformation(ex));
Application::Exit(EXIT_FAILURE);
}
return true;
}
static void IncludeZoneDirRecursive(const String& path)
@ -95,21 +99,17 @@ static void IncludeNonLocalZone(const String& zonePath)
static bool LoadConfigFiles(const boost::program_options::variables_map& vm, const String& appType,
const String& objectsFile = String(), const String& varsfile = String())
{
ConfigCompilerContext::GetInstance()->Reset();
if (!objectsFile.IsEmpty())
ConfigCompilerContext::GetInstance()->OpenObjectsFile(objectsFile);
if (vm.count("config") > 0) {
BOOST_FOREACH(const String& configPath, vm["config"].as<std::vector<std::string> >()) {
Expression *expression = ConfigCompiler::CompileFile(configPath);
if (expression)
ExecuteExpression(expression);
delete expression;
}
} else if (!vm.count("no-config")) {
Expression *expression = ConfigCompiler::CompileFile(Application::GetSysconfDir() + "/icinga2/icinga2.conf");
if (expression)
ExecuteExpression(expression);
delete expression;
}
@ -127,7 +127,6 @@ static bool LoadConfigFiles(const boost::program_options::variables_map& vm, con
String name, fragment;
BOOST_FOREACH(boost::tie(name, fragment), ConfigFragmentRegistry::GetInstance()->GetItems()) {
Expression *expression = ConfigCompiler::CompileText(name, fragment);
if (expression)
ExecuteExpression(expression);
delete expression;
}
@ -138,42 +137,7 @@ static bool LoadConfigFiles(const boost::program_options::variables_map& vm, con
ConfigItem::Ptr item = builder->Compile();
item->Register();
bool result = ConfigItem::ValidateItems();
int warnings = 0, errors = 0;
BOOST_FOREACH(const ConfigCompilerMessage& message, ConfigCompilerContext::GetInstance()->GetMessages()) {
std::ostringstream locbuf;
ShowCodeFragment(locbuf, message.Location);
String location = locbuf.str();
String logmsg;
if (!location.IsEmpty())
logmsg = "Location:\n" + location;
logmsg += String("\nConfig ") + (message.Error ? "error" : "warning") + ": " + message.Text;
if (message.Error) {
Log(LogCritical, "config", logmsg);
errors++;
} else {
Log(LogWarning, "config", logmsg);
warnings++;
}
}
if (warnings > 0 || errors > 0) {
LogSeverity severity;
if (errors == 0)
severity = LogWarning;
else
severity = LogCritical;
Log(severity, "config")
<< errors << " errors, " << warnings << " warnings.";
}
bool result = ConfigItem::CommitItems();
if (!result)
return false;

View File

@ -19,7 +19,6 @@
#include "cli/replcommand.hpp"
#include "config/configcompiler.hpp"
#include "config/configcompilercontext.hpp"
#include "base/json.hpp"
#include "base/console.hpp"
#include "base/application.hpp"
@ -132,22 +131,11 @@ int ReplCommand::Run(const po::variables_map& vm, const std::vector<std::string>
Expression *expr;
try {
ConfigCompilerContext::GetInstance()->Reset();
lines[fileName] = line;
expr = ConfigCompiler::CompileText(fileName, line);
bool has_errors = false;
BOOST_FOREACH(const ConfigCompilerMessage& message, ConfigCompilerContext::GetInstance()->GetMessages()) {
if (message.Error)
has_errors = true;
std::cout << (message.Error ? "Error" : "Warning") << ": " << message.Text << "\n";
}
if (expr && !has_errors) {
if (expr) {
Value result = expr->Evaluate(frame);
std::cout << ConsoleColorTag(Console_ForegroundCyan);
if (!result.IsObject() || result.IsObjectType<Array>() || result.IsObjectType<Dictionary>())

View File

@ -229,8 +229,6 @@ bool RepositoryUtility::AddObject(const String& name, const String& type, const
change->Set("command", "add");
change->Set("attrs", attrs);
ConfigCompilerContext::GetInstance()->Reset();
String fname, fragment;
BOOST_FOREACH(boost::tie(fname, fragment), ConfigFragmentRegistry::GetInstance()->GetItems()) {
Expression *expression = ConfigCompiler::CompileText(fname, fragment);
@ -258,26 +256,14 @@ bool RepositoryUtility::AddObject(const String& name, const String& type, const
Object::Ptr object = dtype->Instantiate();
Deserialize(object, vattrs, false, FAConfig);
try {
RepositoryTypeRuleUtilities utils;
ctype->ValidateItem(name, object, DebugInfo(), &utils);
int warnings = 0, errors = 0;
BOOST_FOREACH(const ConfigCompilerMessage& message, ConfigCompilerContext::GetInstance()->GetMessages()) {
String logmsg = String("Config ") + (message.Error ? "error" : "warning") + ": " + message.Text;
if (message.Error) {
Log(LogCritical, "config", logmsg);
errors++;
} else {
Log(LogWarning, "config", logmsg);
warnings++;
}
}
if (errors > 0)
} catch (const ScriptError& ex) {
Log(LogCritical, "config", DiagnosticInformation(ex));
return false;
}
}
if (CheckChangeExists(change)) {
Log(LogWarning, "cli")

View File

@ -570,8 +570,8 @@ void CompatLogger::ValidateRotationMethod(const String& location, const CompatLo
if (rotation_method != "HOURLY" && rotation_method != "DAILY" &&
rotation_method != "WEEKLY" && rotation_method != "MONTHLY" && rotation_method != "NONE") {
ConfigCompilerContext::GetInstance()->AddMessage(true, "Validation failed for " +
location + ": Rotation method '" + rotation_method + "' is invalid.");
BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " +
location + ": Rotation method '" + rotation_method + "' is invalid.", GetDebugInfo()));
}
}

View File

@ -33,7 +33,7 @@
#include "base/utility.hpp"
#include "base/exception.hpp"
#include "base/dynamictype.hpp"
#include "base/scripterror.hpp"
#include "base/exception.hpp"
#include <sstream>
#include <stack>
#include <boost/foreach.hpp>

View File

@ -27,39 +27,6 @@
using namespace icinga;
void ConfigCompilerContext::AddMessage(bool error, const String& message, const DebugInfo& di)
{
boost::mutex::scoped_lock lock(m_Mutex);
m_Messages.push_back(ConfigCompilerMessage(error, message, di));
}
std::vector<ConfigCompilerMessage> ConfigCompilerContext::GetMessages(void) const
{
boost::mutex::scoped_lock lock(m_Mutex);
return m_Messages;
}
bool ConfigCompilerContext::HasErrors(void) const
{
boost::mutex::scoped_lock lock(m_Mutex);
BOOST_FOREACH(const ConfigCompilerMessage& message, m_Messages) {
if (message.Error)
return true;
}
return false;
}
void ConfigCompilerContext::Reset(void)
{
boost::mutex::scoped_lock lock(m_Mutex);
m_Messages.clear();
}
ConfigCompilerContext *ConfigCompilerContext::GetInstance(void)
{
return Singleton<ConfigCompilerContext>::GetInstance();

View File

@ -47,12 +47,6 @@ struct I2_CONFIG_API ConfigCompilerMessage
class I2_CONFIG_API ConfigCompilerContext
{
public:
void AddMessage(bool error, const String& message, const DebugInfo& di = DebugInfo());
std::vector<ConfigCompilerMessage> GetMessages(void) const;
bool HasErrors(void) const;
void Reset(void);
void OpenObjectsFile(const String& filename);
void WriteObject(const Dictionary::Ptr& object);
void FinishObjectsFile(void);
@ -60,7 +54,6 @@ public:
static ConfigCompilerContext *GetInstance(void);
private:
std::vector<ConfigCompilerMessage> m_Messages;
String m_ObjectsPath;
StdioStream::Ptr m_ObjectsFP;

View File

@ -34,7 +34,7 @@
#include "base/netstring.hpp"
#include "base/serializer.hpp"
#include "base/json.hpp"
#include "base/scripterror.hpp"
#include "base/exception.hpp"
#include <sstream>
#include <fstream>
#include <boost/foreach.hpp>
@ -163,16 +163,10 @@ DynamicObject::Ptr ConfigItem::Commit(bool discard)
DebugHint debugHints;
try {
ScriptFrame frame(dobj);
if (m_Scope)
m_Scope->CopyTo(frame.Locals);
m_Expression->Evaluate(frame, &debugHints);
} catch (const ScriptError& ex) {
ConfigCompilerContext::GetInstance()->AddMessage(true, ex.what(), ex.GetDebugInfo());
} catch (const std::exception& ex) {
ConfigCompilerContext::GetInstance()->AddMessage(true, DiagnosticInformation(ex));
}
if (discard)
m_Expression.reset();
@ -222,18 +216,9 @@ DynamicObject::Ptr ConfigItem::Commit(bool discard)
ConfigType::Ptr ctype = ConfigType::GetByName(GetType());
if (!ctype)
ConfigCompilerContext::GetInstance()->AddMessage(false, "No validation type found for object '" + GetName() + "' of type '" + GetType() + "'");
else {
if (ctype)
TypeRuleUtilities utils;
try {
ctype->ValidateItem(GetName(), dobj, GetDebugInfo(), &utils);
} catch (const ScriptError& ex) {
ConfigCompilerContext::GetInstance()->AddMessage(true, ex.what(), ex.GetDebugInfo());
} catch (const std::exception& ex) {
ConfigCompilerContext::GetInstance()->AddMessage(true, DiagnosticInformation(ex));
}
}
dobj->Register();
@ -287,7 +272,7 @@ ConfigItem::Ptr ConfigItem::GetObject(const String& type, const String& name)
}
}
bool ConfigItem::CommitNewItems(ParallelWorkQueue& upq)
bool ConfigItem::CommitNewItems(WorkQueue& upq)
{
typedef std::pair<ConfigItem::Ptr, bool> ItemPair;
std::vector<ItemPair> items;
@ -320,7 +305,7 @@ bool ConfigItem::CommitNewItems(ParallelWorkQueue& upq)
upq.Join();
if (ConfigCompilerContext::GetInstance()->HasErrors())
if (upq.HasExceptions())
return false;
std::vector<ConfigItem::Ptr> new_items;
@ -340,22 +325,18 @@ bool ConfigItem::CommitNewItems(ParallelWorkQueue& upq)
return true;
}
bool ConfigItem::ValidateItems(void)
bool ConfigItem::CommitItems(void)
{
ParallelWorkQueue upq;
if (ConfigCompilerContext::GetInstance()->HasErrors())
return false;
WorkQueue upq(25000, Application::GetConcurrency());
Log(LogInformation, "ConfigItem", "Committing config items");
if (!CommitNewItems(upq))
if (!CommitNewItems(upq)) {
upq.ReportExceptions("config");
return false;
}
ApplyRule::CheckMatches();
ApplyRule::DiscardRules();
ConfigItem::DiscardItems();
ConfigType::DiscardTypes();
/* log stats for external parsers */
@ -366,14 +347,11 @@ bool ConfigItem::ValidateItems(void)
<< "Checked " << count << " " << type->GetName() << "(s).";
}
return !ConfigCompilerContext::GetInstance()->HasErrors();
return true;
}
bool ConfigItem::ActivateItems(void)
{
if (ConfigCompilerContext::GetInstance()->HasErrors())
return false;
/* restore the previous program state */
try {
DynamicObject::RestoreObjects(Application::GetStatePath());
@ -384,7 +362,7 @@ bool ConfigItem::ActivateItems(void)
Log(LogInformation, "ConfigItem", "Triggering Start signal for config items");
ParallelWorkQueue upq;
WorkQueue upq(25000, Application::GetConcurrency());
BOOST_FOREACH(const DynamicType::Ptr& type, DynamicType::GetTypes()) {
BOOST_FOREACH(const DynamicObject::Ptr& object, type->GetObjects()) {
@ -414,13 +392,6 @@ bool ConfigItem::ActivateItems(void)
return true;
}
void ConfigItem::DiscardItems(void)
{
boost::mutex::scoped_lock lock(m_Mutex);
m_Items.clear();
}
std::vector<ConfigItem::Ptr> ConfigItem::GetItems(const String& type)
{
std::vector<ConfigItem::Ptr> items;

View File

@ -64,9 +64,8 @@ public:
static ConfigItem::Ptr GetObject(const String& type,
const String& name);
static bool ValidateItems(void);
static bool CommitItems(void);
static bool ActivateItems(void);
static void DiscardItems(void);
static std::vector<ConfigItem::Ptr> GetItems(const String& type);
@ -96,7 +95,7 @@ private:
static ConfigItem::Ptr GetObjectUnlocked(const String& type,
const String& name);
static bool CommitNewItems(ParallelWorkQueue& upq);
static bool CommitNewItems(WorkQueue& upq);
};
}

View File

@ -136,14 +136,14 @@ void ConfigType::ValidateAttribute(const String& key, const Value& value,
}
if (overallResult == ValidationUnknownField)
ConfigCompilerContext::GetInstance()->AddMessage(true, "Unknown attribute: " + LocationToString(locations));
BOOST_THROW_EXCEPTION(ScriptError("Unknown attribute: " + LocationToString(locations)));
else if (overallResult == ValidationInvalidType) {
String message = "Invalid value: " + LocationToString(locations);
if (!hint.IsEmpty())
message += ": " + hint;
ConfigCompilerContext::GetInstance()->AddMessage(true, message);
BOOST_THROW_EXCEPTION(ScriptError(message));
}
if (!subRuleLists.empty() && value.IsObject() && !value.IsObjectType<Array>())
@ -164,10 +164,8 @@ void ConfigType::ValidateObject(const Object::Ptr& object,
Value value = VMOps::GetField(object, require);
if (value.IsEmpty() || (value.IsString() && static_cast<String>(value).IsEmpty())) {
ConfigCompilerContext::GetInstance()->AddMessage(true,
"Required attribute is missing: " + LocationToString(locations));
}
if (value.IsEmpty() || (value.IsString() && static_cast<String>(value).IsEmpty()))
BOOST_THROW_EXCEPTION(ScriptError("Required attribute is missing: " + LocationToString(locations)));
locations.pop_back();
}
@ -223,10 +221,8 @@ void ConfigType::ValidateArray(const Array::Ptr& array,
locations.push_back("Attribute '" + require + "'");
if (array->GetLength() < index) {
ConfigCompilerContext::GetInstance()->AddMessage(true,
"Required array index is missing: " + LocationToString(locations));
}
if (array->GetLength() < index)
BOOST_THROW_EXCEPTION(ScriptError("Required array index is missing: " + LocationToString(locations)));
locations.pop_back();
}

View File

@ -24,7 +24,7 @@
#include "base/json.hpp"
#include "base/object.hpp"
#include "base/logger.hpp"
#include "base/scripterror.hpp"
#include "base/exception.hpp"
#include "base/scriptglobal.hpp"
#include <boost/foreach.hpp>
#include <boost/exception_ptr.hpp>

View File

@ -25,7 +25,7 @@
#include "base/array.hpp"
#include "base/dictionary.hpp"
#include "base/scriptfunction.hpp"
#include "base/scripterror.hpp"
#include "base/exception.hpp"
#include "base/scriptframe.hpp"
#include "base/convert.hpp"
#include <boost/foreach.hpp>

View File

@ -30,7 +30,7 @@
#include "base/dictionary.hpp"
#include "base/scriptfunction.hpp"
#include "base/scriptglobal.hpp"
#include "base/scripterror.hpp"
#include "base/exception.hpp"
#include "base/convert.hpp"
#include "base/objectlock.hpp"
#include <boost/foreach.hpp>

View File

@ -424,7 +424,7 @@ void DbConnection::PrepareDatabase(void)
void DbConnection::ValidateFailoverTimeout(const String& location, const DbConnection::Ptr& object)
{
if (object->GetFailoverTimeout() < 60) {
ConfigCompilerContext::GetInstance()->AddMessage(true, "Validation failed for " +
location + ": Failover timeout minimum is 60s.");
BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " +
location + ": Failover timeout minimum is 60s.", GetDebugInfo()));
}
}

View File

@ -115,7 +115,7 @@ void IdoMysqlConnection::ExceptionHandler(boost::exception_ptr exp)
void IdoMysqlConnection::AssertOnWorkQueue(void)
{
ASSERT(boost::this_thread::get_id() == m_QueryQueue.GetThreadId());
ASSERT(m_QueryQueue.IsWorkerThread());
}
void IdoMysqlConnection::Disconnect(void)

View File

@ -116,7 +116,7 @@ void IdoPgsqlConnection::ExceptionHandler(boost::exception_ptr exp)
void IdoPgsqlConnection::AssertOnWorkQueue(void)
{
ASSERT(boost::this_thread::get_id() == m_QueryQueue.GetThreadId());
ASSERT(m_QueryQueue.IsWorkerThread());
}
void IdoPgsqlConnection::Disconnect(void)

View File

@ -270,7 +270,7 @@ Endpoint::Ptr Checkable::GetCommandEndpoint(void) const
void Checkable::ValidateCheckInterval(const String& location, const Checkable::Ptr& object)
{
if (object->GetCheckInterval() <= 0) {
ConfigCompilerContext::GetInstance()->AddMessage(true, "Validation failed for " +
location + ": check_interval must be greater than 0.");
BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " +
location + ": check_interval must be greater than 0.", GetDebugInfo()));
}
}

View File

@ -47,8 +47,8 @@ void Command::SetModifiedAttributes(int flags, const MessageOrigin& origin)
void Command::ValidateAttributes(const String& location, const Command::Ptr& object)
{
if (object->GetArguments() != Empty && !object->GetCommandLine().IsObjectType<Array>()) {
ConfigCompilerContext::GetInstance()->AddMessage(true, "Validation failed for " +
location + ": Attribute 'command' must be an array if the 'arguments' attribute is set.");
BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " +
location + ": Attribute 'command' must be an array if the 'arguments' attribute is set.", GetDebugInfo()));
}
}

View File

@ -27,7 +27,7 @@
#include "base/logger.hpp"
#include "base/context.hpp"
#include "base/workqueue.hpp"
#include "base/scripterror.hpp"
#include "base/exception.hpp"
#include <boost/foreach.hpp>
using namespace icinga;
@ -150,12 +150,8 @@ void Dependency::EvaluateApplyRules(const Host::Ptr& host)
if (rule.GetTargetType() != "Host")
continue;
try {
if (EvaluateApplyRule(host, rule))
rule.AddMatch();
} catch (const ScriptError& ex) {
ConfigCompilerContext::GetInstance()->AddMessage(true, ex.what(), ex.GetDebugInfo());
}
}
}
@ -167,11 +163,7 @@ void Dependency::EvaluateApplyRules(const Service::Ptr& service)
if (rule.GetTargetType() != "Service")
continue;
try {
if (EvaluateApplyRule(service, rule))
rule.AddMatch();
} catch (const ScriptError& ex) {
ConfigCompilerContext::GetInstance()->AddMessage(true, ex.what(), ex.GetDebugInfo());
}
}
}

View File

@ -208,12 +208,12 @@ void Dependency::ValidateFilters(const String& location, const Dependency::Ptr&
int sfilter = FilterArrayToInt(object->GetStates(), 0);
if (object->GetParentServiceName().IsEmpty() && (sfilter & ~(StateFilterUp | StateFilterDown)) != 0) {
ConfigCompilerContext::GetInstance()->AddMessage(true, "Validation failed for " +
location + ": State filter is invalid for host dependency.");
BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " +
location + ": State filter is invalid for host dependency.", GetDebugInfo()));
}
if (!object->GetParentServiceName().IsEmpty() && (sfilter & ~(StateFilterOK | StateFilterWarning | StateFilterCritical | StateFilterUnknown)) != 0) {
ConfigCompilerContext::GetInstance()->AddMessage(true, "Validation failed for " +
location + ": State filter is invalid for service dependency.");
BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " +
location + ": State filter is invalid for service dependency.", GetDebugInfo()));
}
}

View File

@ -23,7 +23,7 @@
#include "icinga/pluginutility.hpp"
#include "icinga/scheduleddowntime.hpp"
#include "config/configcompilercontext.hpp"
#include "base/scripterror.hpp"
#include "base/exception.hpp"
#include "base/objectlock.hpp"
#include "base/convert.hpp"
#include "base/utility.hpp"

View File

@ -27,7 +27,7 @@
#include "base/logger.hpp"
#include "base/context.hpp"
#include "base/workqueue.hpp"
#include "base/scripterror.hpp"
#include "base/exception.hpp"
#include <boost/foreach.hpp>
using namespace icinga;
@ -150,12 +150,8 @@ void Notification::EvaluateApplyRules(const Host::Ptr& host)
if (rule.GetTargetType() != "Host")
continue;
try {
if (EvaluateApplyRule(host, rule))
rule.AddMatch();
} catch (const ScriptError& ex) {
ConfigCompilerContext::GetInstance()->AddMessage(true, ex.what(), ex.GetDebugInfo());
}
}
}
@ -167,11 +163,7 @@ void Notification::EvaluateApplyRules(const Service::Ptr& service)
if (rule.GetTargetType() != "Service")
continue;
try {
if (EvaluateApplyRule(service, rule))
rule.AddMatch();
} catch (const ScriptError& ex) {
ConfigCompilerContext::GetInstance()->AddMessage(true, ex.what(), ex.GetDebugInfo());
}
}
}

View File

@ -501,13 +501,13 @@ void Notification::ValidateFilters(const String& location, const Notification::P
int sfilter = FilterArrayToInt(object->GetStates(), 0);
if (object->GetServiceName().IsEmpty() && (sfilter & ~(StateFilterUp | StateFilterDown)) != 0) {
ConfigCompilerContext::GetInstance()->AddMessage(true, "Validation failed for " +
location + ": State filter is invalid.");
BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " +
location + ": State filter is invalid.", GetDebugInfo()));
}
if (!object->GetServiceName().IsEmpty() && (sfilter & ~(StateFilterOK | StateFilterWarning | StateFilterCritical | StateFilterUnknown)) != 0) {
ConfigCompilerContext::GetInstance()->AddMessage(true, "Validation failed for " +
location + ": State filter is invalid.");
BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " +
location + ": State filter is invalid.", GetDebugInfo()));
}
int tfilter = FilterArrayToInt(object->GetTypes(), 0);
@ -515,8 +515,8 @@ void Notification::ValidateFilters(const String& location, const Notification::P
if ((tfilter & ~(1 << NotificationDowntimeStart | 1 << NotificationDowntimeEnd | 1 << NotificationDowntimeRemoved |
1 << NotificationCustom | 1 << NotificationAcknowledgement | 1 << NotificationProblem | 1 << NotificationRecovery |
1 << NotificationFlappingStart | 1 << NotificationFlappingEnd)) != 0) {
ConfigCompilerContext::GetInstance()->AddMessage(true, "Validation failed for " +
location + ": Type filter is invalid.");
BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " +
location + ": Type filter is invalid.", GetDebugInfo()));
}
}

View File

@ -26,7 +26,7 @@
#include "base/dynamictype.hpp"
#include "base/logger.hpp"
#include "base/context.hpp"
#include "base/scripterror.hpp"
#include "base/exception.hpp"
#include <boost/foreach.hpp>
using namespace icinga;
@ -148,12 +148,8 @@ void ScheduledDowntime::EvaluateApplyRules(const Host::Ptr& host)
if (rule.GetTargetType() != "Host")
continue;
try {
if (EvaluateApplyRule(host, rule))
rule.AddMatch();
} catch (const ScriptError& ex) {
ConfigCompilerContext::GetInstance()->AddMessage(true, ex.what(), ex.GetDebugInfo());
}
}
}
@ -165,11 +161,7 @@ void ScheduledDowntime::EvaluateApplyRules(const Service::Ptr& service)
if (rule.GetTargetType() != "Service")
continue;
try {
if (EvaluateApplyRule(service, rule))
rule.AddMatch();
} catch (const ScriptError& ex) {
ConfigCompilerContext::GetInstance()->AddMessage(true, ex.what(), ex.GetDebugInfo());
}
}
}

View File

@ -26,7 +26,7 @@
#include "base/logger.hpp"
#include "base/context.hpp"
#include "base/workqueue.hpp"
#include "base/scripterror.hpp"
#include "base/exception.hpp"
#include <boost/foreach.hpp>
using namespace icinga;
@ -133,11 +133,7 @@ void Service::EvaluateApplyRules(const Host::Ptr& host)
BOOST_FOREACH(ApplyRule& rule, ApplyRule::GetRules("Service")) {
CONTEXT("Evaluating 'apply' rules for host '" + host->GetName() + "'");
try {
if (EvaluateApplyRule(host, rule))
rule.AddMatch();
} catch (const ScriptError& ex) {
ConfigCompilerContext::GetInstance()->AddMessage(true, ex.what(), ex.GetDebugInfo());
}
}
}

View File

@ -106,8 +106,8 @@ void User::ValidateFilters(const String& location, const Dictionary::Ptr& attrs)
int sfilter = FilterArrayToInt(attrs->Get("states"), 0);
if ((sfilter & ~(StateFilterUp | StateFilterDown | StateFilterOK | StateFilterWarning | StateFilterCritical | StateFilterUnknown)) != 0) {
ConfigCompilerContext::GetInstance()->AddMessage(true, "Validation failed for " +
location + ": State filter is invalid.");
BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " +
location + ": State filter is invalid.", GetDebugInfo()));
}
int tfilter = FilterArrayToInt(attrs->Get("types"), 0);
@ -115,8 +115,8 @@ void User::ValidateFilters(const String& location, const Dictionary::Ptr& attrs)
if ((tfilter & ~(1 << NotificationDowntimeStart | 1 << NotificationDowntimeEnd | 1 << NotificationDowntimeRemoved |
1 << NotificationCustom | 1 << NotificationAcknowledgement | 1 << NotificationProblem | 1 << NotificationRecovery |
1 << NotificationFlappingStart | 1 << NotificationFlappingEnd)) != 0) {
ConfigCompilerContext::GetInstance()->AddMessage(true, "Validation failed for " +
location + ": Type filter is invalid.");
BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " +
location + ": Type filter is invalid.", GetDebugInfo()));
}
}

View File

@ -188,7 +188,7 @@ void LivestatusListener::ValidateSocketType(const String& location, const Livest
String socket_type = object->GetSocketType();
if (socket_type != "unix" && socket_type != "tcp") {
ConfigCompilerContext::GetInstance()->AddMessage(true, "Validation failed for " +
location + ": Socket type '" + socket_type + "' is invalid.");
BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " +
location + ": Socket type '" + socket_type + "' is invalid.", GetDebugInfo()));
}
}