Skip to content

Commit 8280a3b

Browse files
committed
Publish cluster stats to parents every 10s while connected (event::ClusterStats)
1 parent 22e2661 commit 8280a3b

File tree

3 files changed

+251
-0
lines changed

3 files changed

+251
-0
lines changed

lib/remote/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ set(remote_SOURCES
3636
modifyobjecthandler.cpp modifyobjecthandler.hpp
3737
objectqueryhandler.cpp objectqueryhandler.hpp
3838
pkiutility.cpp pkiutility.hpp
39+
statsreporter.cpp statsreporter.hpp
3940
statushandler.cpp statushandler.hpp
4041
templatequeryhandler.cpp templatequeryhandler.hpp
4142
typequeryhandler.cpp typequeryhandler.hpp

lib/remote/statsreporter.cpp

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
/******************************************************************************
2+
* Icinga 2 *
3+
* Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) *
4+
* *
5+
* This program is free software; you can redistribute it and/or *
6+
* modify it under the terms of the GNU General Public License *
7+
* as published by the Free Software Foundation; either version 2 *
8+
* of the License, or (at your option) any later version. *
9+
* *
10+
* This program is distributed in the hope that it will be useful, *
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13+
* GNU General Public License for more details. *
14+
* *
15+
* You should have received a copy of the GNU General Public License *
16+
* along with this program; if not, write to the Free Software Foundation *
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
18+
******************************************************************************/
19+
20+
#include "base/application.hpp"
21+
#include "base/array.hpp"
22+
#include "base/configtype.hpp"
23+
#include "base/dictionary.hpp"
24+
#include "base/function.hpp"
25+
#include "base/objectlock.hpp"
26+
#include "base/scriptglobal.hpp"
27+
#include "base/utility.hpp"
28+
#include "base/value.hpp"
29+
#include "remote/apifunction.hpp"
30+
#include "remote/endpoint.hpp"
31+
#include "remote/messageorigin.hpp"
32+
#include "remote/statsreporter.hpp"
33+
#include "remote/zone.hpp"
34+
#include <boost/thread/mutex.hpp>
35+
#include <set>
36+
37+
using namespace icinga;
38+
39+
REGISTER_APIFUNCTION(ClusterStats, event, &StatsReporter::ClusterStatsAPIHandler);
40+
41+
StatsReporter StatsReporter::m_Instance;
42+
43+
StatsReporter::StatsReporter()
44+
{
45+
Endpoint::OnConnected.connect([this](const Endpoint::Ptr& endpoint, const intrusive_ptr<JsonRpcConnection>&) {
46+
OnConnected(endpoint);
47+
});
48+
}
49+
50+
void StatsReporter::OnConnected(const Endpoint::Ptr& endpoint)
51+
{
52+
if (!m_HasBeenInitialized.test_and_set()) {
53+
timer = Timer::Create();
54+
timer->OnTimerExpired.connect([this](const Timer * const&) { ReportStats(); });
55+
timer->SetInterval(10);
56+
timer->Start();
57+
timer->Reschedule(1);
58+
}
59+
}
60+
61+
void StatsReporter::ReportStats()
62+
{
63+
auto parent (Zone::GetLocalZone()->GetParent());
64+
65+
if (parent) {
66+
Dictionary::Ptr message;
67+
68+
for (auto& endpoint : parent->GetEndpoints()) {
69+
for (auto& client : endpoint->GetClients()) {
70+
if (!message) {
71+
message = new Dictionary({
72+
{ "jsonrpc", "2.0" },
73+
{ "method", "event::ClusterStats" },
74+
{ "params", new Dictionary({
75+
{ "stats", GenerateStats() }
76+
}) }
77+
});
78+
}
79+
80+
client->SendMessage(message);
81+
82+
break;
83+
}
84+
}
85+
}
86+
}
87+
88+
Dictionary::Ptr StatsReporter::GenerateStats()
89+
{
90+
auto allStats (new Dictionary);
91+
92+
{
93+
boost::mutex::scoped_lock lock (m_Mutex);
94+
95+
for (auto& endpointStats : m_SecondaryStats) {
96+
allStats->Set(endpointStats.first, endpointStats.second);
97+
}
98+
}
99+
100+
auto localZone (Zone::GetLocalZone());
101+
auto parentZone (localZone->GetParent());
102+
auto unorderedZones (ConfigType::GetObjectsByType<Zone>());
103+
std::set<Zone::Ptr> zones (unorderedZones.begin(), unorderedZones.end());
104+
std::set<Endpoint::Ptr> endpoints;
105+
auto ourStatus (new Dictionary);
106+
auto now (Utility::GetTime());
107+
108+
unorderedZones.clear();
109+
110+
for (auto zone (zones.begin()); zone != zones.end();) {
111+
if ((*zone)->GetParent() == localZone) {
112+
++zone;
113+
} else {
114+
zones.erase(zone++);
115+
}
116+
}
117+
118+
zones.emplace(localZone);
119+
120+
if (parentZone)
121+
zones.emplace(parentZone);
122+
123+
for (auto& zone : zones) {
124+
auto zoneEndpoints (zone->GetEndpoints());
125+
endpoints.insert(zoneEndpoints.begin(), zoneEndpoints.end());
126+
}
127+
128+
endpoints.erase(Endpoint::GetLocalEndpoint());
129+
130+
for (auto& endpoint : endpoints) {
131+
ourStatus->Set(endpoint->GetName(), new Dictionary({
132+
{"connected", endpoint->GetConnected()},
133+
{"last_message_received", endpoint->GetLastMessageReceived()}
134+
}));
135+
}
136+
137+
allStats->Set(Endpoint::GetLocalEndpoint()->GetName(), new Dictionary({
138+
{"mtime", now},
139+
{"version", Application::GetAppVersion()},
140+
{"uptime", now - Application::GetStartTime()},
141+
{"endpoints", ourStatus}
142+
}));
143+
144+
return allStats;
145+
}
146+
147+
Value StatsReporter::ClusterStatsAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
148+
{
149+
auto endpoint (origin->FromClient->GetEndpoint());
150+
151+
if (endpoint && endpoint->GetZone()->IsChildOf(Zone::GetLocalZone())) {
152+
auto rawStats (params->Get("stats"));
153+
154+
if (rawStats.IsObject()) {
155+
Dictionary::Ptr allStats (rawStats);
156+
157+
if (allStats) {
158+
// Don't permit any child to speak in our zone's name
159+
std::set<String> neighborhood;
160+
161+
for (auto& endpoint : Zone::GetLocalZone()->GetEndpoints()) {
162+
neighborhood.emplace(endpoint->GetName());
163+
}
164+
165+
ObjectLock lock (allStats);
166+
167+
for (auto& endpointStats : allStats) {
168+
if (endpointStats.second.IsObject()) {
169+
Dictionary::Ptr stats (endpointStats.second);
170+
171+
if (stats && neighborhood.find(endpointStats.first) == neighborhood.end()) {
172+
m_Instance.ClusterStatsHandler(endpointStats.first, endpointStats.second);
173+
}
174+
}
175+
}
176+
}
177+
}
178+
}
179+
180+
return Empty;
181+
}
182+
183+
void StatsReporter::ClusterStatsHandler(const String& endpoint, const Dictionary::Ptr& stats)
184+
{
185+
boost::mutex::scoped_lock lock (m_Mutex);
186+
m_SecondaryStats[endpoint] = stats;
187+
}

lib/remote/statsreporter.hpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/******************************************************************************
2+
* Icinga 2 *
3+
* Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) *
4+
* *
5+
* This program is free software; you can redistribute it and/or *
6+
* modify it under the terms of the GNU General Public License *
7+
* as published by the Free Software Foundation; either version 2 *
8+
* of the License, or (at your option) any later version. *
9+
* *
10+
* This program is distributed in the hope that it will be useful, *
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13+
* GNU General Public License for more details. *
14+
* *
15+
* You should have received a copy of the GNU General Public License *
16+
* along with this program; if not, write to the Free Software Foundation *
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
18+
******************************************************************************/
19+
20+
#ifndef STATSREPORTER_H
21+
#define STATSREPORTER_H
22+
23+
#include "base/dictionary.hpp"
24+
#include "base/string.hpp"
25+
#include "base/timer.hpp"
26+
#include "base/value.hpp"
27+
#include "remote/endpoint.hpp"
28+
#include "remote/messageorigin.hpp"
29+
#include <atomic>
30+
#include <boost/thread/mutex.hpp>
31+
#include <map>
32+
33+
namespace icinga
34+
{
35+
36+
/**
37+
* @ingroup remote
38+
*/
39+
class StatsReporter
40+
{
41+
public:
42+
static Value ClusterStatsAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);
43+
44+
private:
45+
StatsReporter();
46+
47+
void OnConnected(const Endpoint::Ptr& endpoint);
48+
void ReportStats();
49+
Dictionary::Ptr GenerateStats();
50+
void ClusterStatsHandler(const String& endpoint, const Dictionary::Ptr& stats);
51+
52+
static StatsReporter m_Instance;
53+
54+
std::atomic_flag m_HasBeenInitialized = ATOMIC_FLAG_INIT;
55+
Timer::Ptr timer;
56+
57+
boost::mutex m_Mutex;
58+
std::map<String, Dictionary::Ptr> m_SecondaryStats;
59+
};
60+
61+
}
62+
63+
#endif /* STATSREPORTER_H */

0 commit comments

Comments
 (0)