1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
3 #include "icinga/comment.hpp"
4 #include "icinga/comment-ti.cpp"
5 #include "icinga/host.hpp"
6 #include "remote/configobjectutility.hpp"
7 #include "base/utility.hpp"
8 #include "base/configtype.hpp"
9 #include "base/timer.hpp"
10 #include <boost/thread/once.hpp>
12 using namespace icinga;
14 static int l_NextCommentID = 1;
15 static boost::mutex l_CommentMutex;
16 static std::map<int, String> l_LegacyCommentsCache;
17 static Timer::Ptr l_CommentsExpireTimer;
19 boost::signals2::signal<void (const Comment::Ptr&)> Comment::OnCommentAdded;
20 boost::signals2::signal<void (const Comment::Ptr&)> Comment::OnCommentRemoved;
22 REGISTER_TYPE(Comment);
24 String CommentNameComposer::MakeName(const String& shortName, const Object::Ptr& context) const
26 Comment::Ptr comment = dynamic_pointer_cast<Comment>(context);
31 String name = comment->GetHostName();
33 if (!comment->GetServiceName().IsEmpty())
34 name += "!" + comment->GetServiceName();
36 name += "!" + shortName;
41 Dictionary::Ptr CommentNameComposer::ParseName(const String& name) const
43 std::vector<String> tokens = name.Split("!");
45 if (tokens.size() < 2)
46 BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid Comment name."));
48 Dictionary::Ptr result = new Dictionary();
49 result->Set("host_name", tokens[0]);
51 if (tokens.size() > 2) {
52 result->Set("service_name", tokens[1]);
53 result->Set("name", tokens[2]);
55 result->Set("name", tokens[1]);
61 void Comment::OnAllConfigLoaded()
63 ConfigObject::OnAllConfigLoaded();
65 Host::Ptr host = Host::GetByName(GetHostName());
67 if (GetServiceName().IsEmpty())
70 m_Checkable = host->GetServiceByShortName(GetServiceName());
73 BOOST_THROW_EXCEPTION(ScriptError("Comment '" + GetName() + "' references a host/service which doesn't exist.", GetDebugInfo()));
76 void Comment::Start(bool runtimeCreated)
78 ObjectImpl<Comment>::Start(runtimeCreated);
80 static boost::once_flag once = BOOST_ONCE_INIT;
82 boost::call_once(once, [this]() {
83 l_CommentsExpireTimer = new Timer();
84 l_CommentsExpireTimer->SetInterval(60);
85 l_CommentsExpireTimer->OnTimerExpired.connect(std::bind(&Comment::CommentsExpireTimerHandler));
86 l_CommentsExpireTimer->Start();
90 boost::mutex::scoped_lock lock(l_CommentMutex);
92 SetLegacyId(l_NextCommentID);
93 l_LegacyCommentsCache[l_NextCommentID] = GetName();
97 GetCheckable()->RegisterComment(this);
100 OnCommentAdded(this);
103 void Comment::Stop(bool runtimeRemoved)
105 GetCheckable()->UnregisterComment(this);
108 OnCommentRemoved(this);
110 ObjectImpl<Comment>::Stop(runtimeRemoved);
113 Checkable::Ptr Comment::GetCheckable() const
115 return static_pointer_cast<Checkable>(m_Checkable);
118 bool Comment::IsExpired() const
120 double expire_time = GetExpireTime();
122 return (expire_time != 0 && expire_time < Utility::GetTime());
125 int Comment::GetNextCommentID()
127 boost::mutex::scoped_lock lock(l_CommentMutex);
129 return l_NextCommentID;
132 String Comment::AddComment(const Checkable::Ptr& checkable, CommentType entryType, const String& author,
133 const String& text, bool persistent, double expireTime, const String& id, const MessageOrigin::Ptr& origin)
138 fullName = checkable->GetName() + "!" + Utility::NewUniqueID();
142 Dictionary::Ptr attrs = new Dictionary();
144 attrs->Set("author", author);
145 attrs->Set("text", text);
146 attrs->Set("persistent", persistent);
147 attrs->Set("expire_time", expireTime);
148 attrs->Set("entry_type", entryType);
149 attrs->Set("entry_time", Utility::GetTime());
152 Service::Ptr service;
153 tie(host, service) = GetHostService(checkable);
155 attrs->Set("host_name", host->GetName());
157 attrs->Set("service_name", service->GetShortName());
159 String zone = checkable->GetZoneName();
162 attrs->Set("zone", zone);
164 String config = ConfigObjectUtility::CreateObjectConfig(Comment::TypeInstance, fullName, true, nullptr, attrs);
166 Array::Ptr errors = new Array();
168 if (!ConfigObjectUtility::CreateObject(Comment::TypeInstance, fullName, config, errors, nullptr)) {
169 ObjectLock olock(errors);
170 for (const String& error : errors) {
171 Log(LogCritical, "Comment", error);
174 BOOST_THROW_EXCEPTION(std::runtime_error("Could not create comment."));
177 Comment::Ptr comment = Comment::GetByName(fullName);
180 BOOST_THROW_EXCEPTION(std::runtime_error("Could not create comment."));
182 Log(LogNotice, "Comment")
183 << "Added comment '" << comment->GetName() << "'.";
188 void Comment::RemoveComment(const String& id, const MessageOrigin::Ptr& origin)
190 Comment::Ptr comment = Comment::GetByName(id);
192 if (!comment || comment->GetPackage() != "_api")
195 Log(LogNotice, "Comment")
196 << "Removed comment '" << comment->GetName() << "' from object '" << comment->GetCheckable()->GetName() << "'.";
198 Array::Ptr errors = new Array();
200 if (!ConfigObjectUtility::DeleteObject(comment, false, errors, nullptr)) {
201 ObjectLock olock(errors);
202 for (const String& error : errors) {
203 Log(LogCritical, "Comment", error);
206 BOOST_THROW_EXCEPTION(std::runtime_error("Could not remove comment."));
210 String Comment::GetCommentIDFromLegacyID(int id)
212 boost::mutex::scoped_lock lock(l_CommentMutex);
214 auto it = l_LegacyCommentsCache.find(id);
216 if (it == l_LegacyCommentsCache.end())
222 void Comment::CommentsExpireTimerHandler()
224 std::vector<Comment::Ptr> comments;
226 for (const Comment::Ptr& comment : ConfigType::GetObjectsByType<Comment>()) {
227 comments.push_back(comment);
230 for (const Comment::Ptr& comment : comments) {
231 /* Only remove comments which are activated after daemon start. */
232 if (comment->IsActive() && comment->IsExpired()) {
233 /* Do not remove persistent comments from an acknowledgement */
234 if (comment->GetEntryType() == CommentAcknowledgement && comment->GetPersistent())
237 RemoveComment(comment->GetName());