diff --git a/lib/icinga/comment.cpp b/lib/icinga/comment.cpp index c23e14619..9931b42bd 100644 --- a/lib/icinga/comment.cpp +++ b/lib/icinga/comment.cpp @@ -182,7 +182,7 @@ String Comment::AddComment(const Checkable::Ptr& checkable, CommentType entryTyp Array::Ptr errors = new Array(); - if (!ConfigObjectUtility::CreateObject(Comment::TypeInstance, fullName, config, errors)) { + if (!ConfigObjectUtility::CreateObject(Comment::TypeInstance, fullName, config, errors, nullptr)) { ObjectLock olock(errors); for (const String& error : errors) { Log(LogCritical, "Comment", error); @@ -214,7 +214,7 @@ void Comment::RemoveComment(const String& id, const MessageOrigin::Ptr& origin) Array::Ptr errors = new Array(); - if (!ConfigObjectUtility::DeleteObject(comment, false, errors)) { + if (!ConfigObjectUtility::DeleteObject(comment, false, errors, nullptr)) { ObjectLock olock(errors); for (const String& error : errors) { Log(LogCritical, "Comment", error); diff --git a/lib/icinga/downtime.cpp b/lib/icinga/downtime.cpp index a52358359..73e41c2a8 100644 --- a/lib/icinga/downtime.cpp +++ b/lib/icinga/downtime.cpp @@ -256,7 +256,7 @@ String Downtime::AddDowntime(const Checkable::Ptr& checkable, const String& auth Array::Ptr errors = new Array(); - if (!ConfigObjectUtility::CreateObject(Downtime::TypeInstance, fullName, config, errors)) { + if (!ConfigObjectUtility::CreateObject(Downtime::TypeInstance, fullName, config, errors, nullptr)) { ObjectLock olock(errors); for (const String& error : errors) { Log(LogCritical, "Downtime", error); @@ -309,7 +309,7 @@ void Downtime::RemoveDowntime(const String& id, bool cancelled, bool expired, co Array::Ptr errors = new Array(); - if (!ConfigObjectUtility::DeleteObject(downtime, false, errors)) { + if (!ConfigObjectUtility::DeleteObject(downtime, false, errors, nullptr)) { ObjectLock olock(errors); for (const String& error : errors) { Log(LogCritical, "Downtime", error); diff --git a/lib/remote/apilistener-configsync.cpp b/lib/remote/apilistener-configsync.cpp index 067e2819c..12c91065e 100644 --- a/lib/remote/apilistener-configsync.cpp +++ b/lib/remote/apilistener-configsync.cpp @@ -116,15 +116,14 @@ Value ApiListener::ConfigUpdateObjectAPIHandler(const MessageOrigin::Ptr& origin /* object does not exist, create it through the API */ Array::Ptr errors = new Array(); - if (!ConfigObjectUtility::CreateObject(ptype, - objName, config, errors)) { + if (!ConfigObjectUtility::CreateObject(ptype, objName, config, errors, nullptr)) { Log(LogCritical, "ApiListener") << "Could not create object '" << objName << "':"; - ObjectLock olock(errors); + ObjectLock olock(errors); for (const String& error : errors) { - Log(LogCritical, "ApiListener", error); - } + Log(LogCritical, "ApiListener", error); + } return Empty; } @@ -256,7 +255,7 @@ Value ApiListener::ConfigDeleteObjectAPIHandler(const MessageOrigin::Ptr& origin Array::Ptr errors = new Array(); - if (!ConfigObjectUtility::DeleteObject(object, true, errors)) { + if (!ConfigObjectUtility::DeleteObject(object, true, errors, nullptr)) { Log(LogCritical, "ApiListener", "Could not delete object:"); ObjectLock olock(errors); diff --git a/lib/remote/configobjectutility.cpp b/lib/remote/configobjectutility.cpp index 38fda3824..3ad88c675 100644 --- a/lib/remote/configobjectutility.cpp +++ b/lib/remote/configobjectutility.cpp @@ -98,7 +98,7 @@ String ConfigObjectUtility::CreateObjectConfig(const Type::Ptr& type, const Stri } bool ConfigObjectUtility::CreateObject(const Type::Ptr& type, const String& fullName, - const String& config, const Array::Ptr& errors) + const String& config, const Array::Ptr& errors, const Array::Ptr& diagnosticInformation) { { boost::mutex::scoped_lock lock(ConfigPackageUtility::GetStaticMutex()); @@ -114,7 +114,7 @@ bool ConfigObjectUtility::CreateObject(const Type::Ptr& type, const String& full Utility::MkDirP(Utility::DirName(path), 0700); if (Utility::PathExists(path)) { - errors->Add("Configuration file '" + path + "' already exists."); + errors->Add("Cannot create object '" + fullName + "'. Configuration file '" + path + "' already exists."); return false; } @@ -144,7 +144,10 @@ bool ConfigObjectUtility::CreateObject(const Type::Ptr& type, const String& full } for (const boost::exception_ptr& ex : upq.GetExceptions()) { - errors->Add(DiagnosticInformation(ex)); + errors->Add(DiagnosticInformation(ex, false)); + + if (diagnosticInformation) + diagnosticInformation->Add(DiagnosticInformation(ex)); } } @@ -161,7 +164,10 @@ bool ConfigObjectUtility::CreateObject(const Type::Ptr& type, const String& full } if (errors) - errors->Add(DiagnosticInformation(ex)); + errors->Add(DiagnosticInformation(ex, false)); + + if (diagnosticInformation) + diagnosticInformation->Add(DiagnosticInformation(ex)); return false; } @@ -169,17 +175,21 @@ bool ConfigObjectUtility::CreateObject(const Type::Ptr& type, const String& full return true; } -bool ConfigObjectUtility::DeleteObjectHelper(const ConfigObject::Ptr& object, bool cascade, const Array::Ptr& errors) +bool ConfigObjectUtility::DeleteObjectHelper(const ConfigObject::Ptr& object, bool cascade, + const Array::Ptr& errors, const Array::Ptr& diagnosticInformation) { std::vector parents = DependencyGraph::GetParents(object); Type::Ptr type = object->GetReflectionType(); + String name = object->GetName(); + if (!parents.empty() && !cascade) { - if (errors) - errors->Add("Object '" + object->GetName() + "' of type '" + type->GetName() + + if (errors) { + errors->Add("Object '" + name + "' of type '" + type->GetName() + "' cannot be deleted because other objects depend on it. " "Use cascading delete to delete it anyway."); + } return false; } @@ -190,10 +200,10 @@ bool ConfigObjectUtility::DeleteObjectHelper(const ConfigObject::Ptr& object, bo if (!parentObj) continue; - DeleteObjectHelper(parentObj, cascade, errors); + DeleteObjectHelper(parentObj, cascade, errors, diagnosticInformation); } - ConfigItem::Ptr item = ConfigItem::GetByTypeAndName(type, object->GetName()); + ConfigItem::Ptr item = ConfigItem::GetByTypeAndName(type, name); try { /* mark this object for cluster delete event */ @@ -208,12 +218,15 @@ bool ConfigObjectUtility::DeleteObjectHelper(const ConfigObject::Ptr& object, bo } catch (const std::exception& ex) { if (errors) - errors->Add(DiagnosticInformation(ex)); + errors->Add(DiagnosticInformation(ex, false)); + + if (diagnosticInformation) + diagnosticInformation->Add(DiagnosticInformation(ex)); return false; } - String path = GetObjectConfigPath(object->GetReflectionType(), object->GetName()); + String path = GetObjectConfigPath(object->GetReflectionType(), name); if (Utility::PathExists(path)) { if (unlink(path.CStr()) < 0 && errno != ENOENT) { @@ -227,7 +240,7 @@ bool ConfigObjectUtility::DeleteObjectHelper(const ConfigObject::Ptr& object, bo return true; } -bool ConfigObjectUtility::DeleteObject(const ConfigObject::Ptr& object, bool cascade, const Array::Ptr& errors) +bool ConfigObjectUtility::DeleteObject(const ConfigObject::Ptr& object, bool cascade, const Array::Ptr& errors, const Array::Ptr& diagnosticInformation) { if (object->GetPackage() != "_api") { if (errors) @@ -236,5 +249,5 @@ bool ConfigObjectUtility::DeleteObject(const ConfigObject::Ptr& object, bool cas return false; } - return DeleteObjectHelper(object, cascade, errors); + return DeleteObjectHelper(object, cascade, errors, diagnosticInformation); } diff --git a/lib/remote/configobjectutility.hpp b/lib/remote/configobjectutility.hpp index ea42ff3f2..472af3c83 100644 --- a/lib/remote/configobjectutility.hpp +++ b/lib/remote/configobjectutility.hpp @@ -45,13 +45,15 @@ public: bool ignoreOnError, const Array::Ptr& templates, const Dictionary::Ptr& attrs); static bool CreateObject(const Type::Ptr& type, const String& fullName, - const String& config, const Array::Ptr& errors); + const String& config, const Array::Ptr& errors, const Array::Ptr& diagnosticInformation); - static bool DeleteObject(const ConfigObject::Ptr& object, bool cascade, const Array::Ptr& errors); + static bool DeleteObject(const ConfigObject::Ptr& object, bool cascade, const Array::Ptr& errors, + const Array::Ptr& diagnosticInformation); private: static String EscapeName(const String& name); - static bool DeleteObjectHelper(const ConfigObject::Ptr& object, bool cascade, const Array::Ptr& errors); + static bool DeleteObjectHelper(const ConfigObject::Ptr& object, bool cascade, const Array::Ptr& errors, + const Array::Ptr& diagnosticInformation); }; } diff --git a/lib/remote/createobjecthandler.cpp b/lib/remote/createobjecthandler.cpp index ff90317af..e3646b3f3 100644 --- a/lib/remote/createobjecthandler.cpp +++ b/lib/remote/createobjecthandler.cpp @@ -74,6 +74,7 @@ bool CreateObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r Dictionary::Ptr result1 = new Dictionary(); String status; Array::Ptr errors = new Array(); + Array::Ptr diagnosticInformation = new Array(); bool ignoreOnError = false; @@ -86,10 +87,22 @@ bool CreateObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r String config; + bool verbose = false; + + if (params) + verbose = HttpUtility::GetLastParameter(params, "verbose"); + + /* Object creation can cause multiple errors and optionally diagnostic information. + * We can't use SendJsonError() here. + */ try { config = ConfigObjectUtility::CreateObjectConfig(type, name, ignoreOnError, templates, attrs); } catch (const std::exception& ex) { - errors->Add(DiagnosticInformation(ex)); + errors->Add(DiagnosticInformation(ex, false)); + diagnosticInformation->Add(DiagnosticInformation(ex)); + + if (verbose) + result1->Set("diagnostic_information", diagnosticInformation); result1->Set("errors", errors); result1->Set("code", 500); @@ -101,11 +114,14 @@ bool CreateObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r return true; } - if (!ConfigObjectUtility::CreateObject(type, name, config, errors)) { + if (!ConfigObjectUtility::CreateObject(type, name, config, errors, diagnosticInformation)) { result1->Set("errors", errors); result1->Set("code", 500); result1->Set("status", "Object could not be created."); + if (verbose) + result1->Set("diagnostic_information", diagnosticInformation); + response.SetStatus(500, "Object could not be created"); HttpUtility::SendJsonBody(response, params, result); diff --git a/lib/remote/deleteobjecthandler.cpp b/lib/remote/deleteobjecthandler.cpp index 027f62fed..0d4053df3 100644 --- a/lib/remote/deleteobjecthandler.cpp +++ b/lib/remote/deleteobjecthandler.cpp @@ -65,11 +65,12 @@ bool DeleteObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r } catch (const std::exception& ex) { HttpUtility::SendJsonError(response, params, 404, "No objects found.", - HttpUtility::GetLastParameter(params, "verboseErrors") ? DiagnosticInformation(ex) : ""); + DiagnosticInformation(ex)); return true; } bool cascade = HttpUtility::GetLastParameter(params, "cascade"); + bool verbose = HttpUtility::GetLastParameter(params, "verbose"); ArrayData results; @@ -79,8 +80,9 @@ bool DeleteObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r int code; String status; Array::Ptr errors = new Array(); + Array::Ptr diagnosticInformation = new Array(); - if (!ConfigObjectUtility::DeleteObject(obj, cascade, errors)) { + if (!ConfigObjectUtility::DeleteObject(obj, cascade, errors, diagnosticInformation)) { code = 500; status = "Object could not be deleted."; success = false; @@ -89,13 +91,18 @@ bool DeleteObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r status = "Object was deleted."; } - results.push_back(new Dictionary({ + Dictionary::Ptr result = new Dictionary({ { "type", type->GetName() }, { "name", obj->GetName() }, { "code", code }, { "status", status }, { "errors", errors } - })); + }); + + if (verbose) + result->Set("diagnostic_information", diagnosticInformation); + + results.push_back(result); } Dictionary::Ptr result = new Dictionary({ diff --git a/lib/remote/modifyobjecthandler.cpp b/lib/remote/modifyobjecthandler.cpp index 893360671..603207ebb 100644 --- a/lib/remote/modifyobjecthandler.cpp +++ b/lib/remote/modifyobjecthandler.cpp @@ -77,6 +77,11 @@ bool ModifyObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r Dictionary::Ptr attrs = attrsVal; + bool verbose = false; + + if (params) + verbose = HttpUtility::GetLastParameter(params, "verbose"); + ArrayData results; for (const ConfigObject::Ptr& obj : objs) { @@ -100,7 +105,10 @@ bool ModifyObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r result1->Set("status", "Attributes updated."); } catch (const std::exception& ex) { result1->Set("code", 500); - result1->Set("status", "Attribute '" + key + "' could not be set: " + DiagnosticInformation(ex)); + result1->Set("status", "Attribute '" + key + "' could not be set: " + DiagnosticInformation(ex, false)); + + if (verbose) + result1->Set("diagnostic_information", DiagnosticInformation(ex)); } results.push_back(std::move(result1));