Skip to content

Commit 6e0324f

Browse files
committed
exporter: SIGTERM/SIGINT/SIGHUP management
Ceph exporter manages SIGINT, SIGTERM and SIGHUP signals Fixes: https://tracker.ceph.com/issues/68721 Signed-off-by: Juan Miguel Olmo Martínez <[email protected]>
1 parent f2a7ed4 commit 6e0324f

File tree

5 files changed

+97
-27
lines changed

5 files changed

+97
-27
lines changed

src/exporter/DaemonMetricCollector.cc

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,16 @@ using json_object = boost::json::object;
2929
using json_value = boost::json::value;
3030
using json_array = boost::json::array;
3131

32-
void DaemonMetricCollector::request_loop(boost::asio::steady_timer &timer) {
33-
timer.async_wait([&](const boost::system::error_code &e) {
34-
std::cerr << e << std::endl;
32+
void DaemonMetricCollector::request_loop() {
33+
timer.async_wait([this](const boost::system::error_code &e) {
34+
if (shutdown_flag) {
35+
dout(1) << "Metric collector request loop cancelled" << dendl;
36+
return;
37+
}
38+
39+
if (e) return; // Exit on error or cancellation
40+
41+
dout(10) << "Getting metrics loop..." << dendl;
3542
update_sockets();
3643

3744
bool sort_metrics = g_conf().get_val<bool>("exporter_sort_metrics");
@@ -42,19 +49,24 @@ void DaemonMetricCollector::request_loop(boost::asio::steady_timer &timer) {
4249
auto stats_period = g_conf().get_val<int64_t>("exporter_stats_period");
4350
// time to wait before sending requests again
4451
timer.expires_from_now(std::chrono::seconds(stats_period));
45-
request_loop(timer);
52+
request_loop();
4653
});
4754
}
4855

4956
void DaemonMetricCollector::main() {
50-
// time to wait before sending requests again
51-
52-
boost::asio::io_context io;
53-
boost::asio::steady_timer timer{io, std::chrono::seconds(0)};
54-
request_loop(timer);
57+
shutdown_flag = false;
58+
timer.expires_from_now(std::chrono::seconds(0));
59+
request_loop();
5560
io.run();
5661
}
5762

63+
void DaemonMetricCollector::shutdown(){
64+
shutdown_flag = true;
65+
timer.cancel(); // Explicitly cancel the timer
66+
dout(1) << "Collector shutdown initiated, timer canceled" << dendl;
67+
io.stop();
68+
}
69+
5870
std::string DaemonMetricCollector::get_metrics() {
5971
const std::lock_guard<std::mutex> lock(metrics_mutex);
6072
return metrics;
@@ -499,3 +511,4 @@ DaemonMetricCollector &collector_instance() {
499511
static DaemonMetricCollector instance;
500512
return instance;
501513
}
514+

src/exporter/DaemonMetricCollector.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
#pragma once
22

33
#include "common/admin_socket_client.h"
4+
#include <atomic>
45
#include <map>
56
#include <string>
67
#include <vector>
78

89
#include <boost/asio/steady_timer.hpp>
10+
#include <boost/thread.hpp>
911
#include <boost/json/object.hpp>
1012
#include <filesystem>
1113
#include <map>
1214
#include <string>
1315
#include <vector>
1416

17+
1518
struct pstat {
1619
unsigned long utime;
1720
unsigned long stime;
@@ -43,11 +46,16 @@ class DaemonMetricCollector {
4346
std::string metrics;
4447
std::pair<labels_t, std::string> add_fixed_name_metrics(std::string metric_name);
4548
void update_sockets();
49+
void shutdown();
4650

4751
private:
4852
std::mutex metrics_mutex;
4953
std::unique_ptr<MetricsBuilder> builder;
50-
void request_loop(boost::asio::steady_timer &timer);
54+
boost::asio::io_context io;
55+
boost::asio::steady_timer timer{io};
56+
std::atomic<bool> shutdown_flag{false};
57+
58+
void request_loop();
5159

5260
void dump_asok_metric(boost::json::object perf_info,
5361
boost::json::value perf_values, std::string name,
@@ -108,3 +116,4 @@ class UnorderedMetricsBuilder : public MetricsBuilder {
108116
};
109117

110118
DaemonMetricCollector &collector_instance();
119+

src/exporter/ceph_exporter.cc

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,31 @@
11
#include "common/ceph_argparse.h"
22
#include "common/config.h"
3-
#include "exporter/DaemonMetricCollector.h"
4-
#include "exporter/web_server.h"
3+
#include "common/debug.h"
54
#include "global/global_init.h"
65
#include "global/global_context.h"
7-
6+
#include "global/signal_handler.h"
7+
#include "exporter/DaemonMetricCollector.h"
8+
#include "exporter/web_server.h"
89
#include <boost/thread/thread.hpp>
910
#include <iostream>
1011
#include <map>
1112
#include <string>
13+
#include <atomic>
14+
#include <chrono>
15+
#include <thread>
1216

1317
#define dout_context g_ceph_context
18+
#define dout_subsys ceph_subsys_ceph_exporter
19+
20+
DaemonMetricCollector &collector = collector_instance();
21+
22+
static void handle_signal(int signum)
23+
{
24+
ceph_assert(signum == SIGINT || signum == SIGTERM);
25+
derr << "*** Got signal " << sig_str(signum) << " ***" << dendl;
26+
// Finish the DaemonMetricCollector
27+
collector.shutdown();
28+
}
1429

1530
static void usage() {
1631
std::cout << "usage: ceph-exporter [options]\n"
@@ -27,7 +42,6 @@ static void usage() {
2742
}
2843

2944
int main(int argc, char **argv) {
30-
3145
auto args = argv_to_vec(argc, argv);
3246
if (args.empty()) {
3347
std::cerr << argv[0] << ": -h or --help for usage" << std::endl;
@@ -64,8 +78,30 @@ int main(int argc, char **argv) {
6478
}
6579
common_init_finish(g_ceph_context);
6680

81+
// Register signal handlers
82+
init_async_signal_handler();
83+
register_async_signal_handler(SIGHUP, sighup_handler);
84+
register_async_signal_handler_oneshot(SIGINT, handle_signal);
85+
register_async_signal_handler_oneshot(SIGTERM, handle_signal);
86+
87+
// Start the web server thread
6788
boost::thread server_thread(web_server_thread_entrypoint);
68-
DaemonMetricCollector &collector = collector_instance();
89+
90+
// Start the DaemonMetricCollector
6991
collector.main();
92+
93+
// Interrupted. Time to terminate
94+
unregister_async_signal_handler(SIGHUP, sighup_handler);
95+
unregister_async_signal_handler(SIGINT, handle_signal);
96+
unregister_async_signal_handler(SIGTERM, handle_signal);
97+
shutdown_async_signal_handler();
98+
99+
// Stop the web server thread by interrupting it
100+
stop_web_server();
101+
server_thread.interrupt(); // Interrupt the web server thread
70102
server_thread.join();
103+
104+
dout(1) << "Ceph exporter stopped" << dendl;
105+
106+
return 0;
71107
}

src/exporter/web_server.cc

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ namespace net = boost::asio; // from <boost/asio.hpp>
2828
namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp>
2929
using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
3030

31+
//common io context for the web servers
32+
std::shared_ptr<net::io_context> global_ioc;
33+
3134
// Base class for common functionality
3235
class web_connection {
3336
public:
@@ -43,7 +46,7 @@ class web_connection {
4346
web_connection(net::any_io_executor executor, std::chrono::seconds timeout)
4447
: deadline_(executor, timeout) {}
4548

46-
// Common request processing logic
49+
// Common request processing logic
4750
void process_request() {
4851
response_.version(request_.version());
4952
response_.keep_alive(request_.keep_alive());
@@ -64,7 +67,7 @@ class web_connection {
6467
write_response();
6568
}
6669

67-
// Construct a response message based on the request target
70+
// Construct a response message based on the request target
6871
void create_response() {
6972
if (request_.target() == "/") {
7073
response_.result(http::status::moved_permanently);
@@ -81,7 +84,7 @@ class web_connection {
8184
}
8285
}
8386

84-
// Asynchronously transmit the response message
87+
// Asynchronously transmit the response message
8588
virtual void write_response() = 0;
8689

8790
// Check whether we have spent enough time on this connection
@@ -228,28 +231,33 @@ void https_server(tcp::acceptor &acceptor, ssl::context &ssl_ctx) {
228231
}
229232

230233
void run_http_server(const std::string& exporter_addr, short unsigned int port) {
231-
net::io_context ioc{1};
232-
tcp::acceptor acceptor{ioc, {net::ip::make_address(exporter_addr), port}};
233-
tcp::socket socket{ioc};
234+
tcp::acceptor acceptor{*global_ioc, {net::ip::make_address(exporter_addr), port}};
235+
tcp::socket socket{*global_ioc};
234236

235237
http_server(acceptor, socket);
236238

237239
dout(1) << "HTTP server running on " << exporter_addr << ":" << port << dendl;
238-
ioc.run();
240+
global_ioc->run();
239241
}
240242

241243
void run_https_server(const std::string& exporter_addr, short unsigned int port, const std::string& cert_file, const std::string& key_file) {
242-
net::io_context ioc{1};
243244
ssl::context ssl_ctx(ssl::context::tlsv13);
244245

245246
ssl_ctx.use_certificate_chain_file(cert_file);
246247
ssl_ctx.use_private_key_file(key_file, ssl::context::pem);
247248

248-
tcp::acceptor acceptor{ioc, {net::ip::make_address(exporter_addr), port}};
249+
tcp::acceptor acceptor{*global_ioc, {net::ip::make_address(exporter_addr), port}};
249250
https_server(acceptor, ssl_ctx);
250251

251252
dout(1) << "HTTPS server running on " << exporter_addr << ":" << port << dendl;
252-
ioc.run();
253+
global_ioc->run();
254+
}
255+
256+
void stop_web_server() {
257+
if (global_ioc) {
258+
global_ioc->stop();
259+
dout(1) << "Ceph exporter web server stopped" << dendl;
260+
}
253261
}
254262

255263
void web_server_thread_entrypoint() {
@@ -259,18 +267,21 @@ void web_server_thread_entrypoint() {
259267
std::string cert_file = g_conf().get_val<std::string>("exporter_cert_file");
260268
std::string key_file = g_conf().get_val<std::string>("exporter_key_file");
261269

270+
// Initialize global_ioc
271+
global_ioc = std::make_shared<net::io_context>(1);
272+
262273
if (cert_file.empty() && key_file.empty()) {
263274
run_http_server(exporter_addr, port);
264275
} else {
265276
try {
266277
run_https_server(exporter_addr, port, cert_file, key_file);
267278
} catch (const std::exception &e) {
268-
dout(1) << "Failed to start HTTPS server: " << e.what() << dendl;
279+
derr << "Failed to start HTTPS server: " << e.what() << dendl;
269280
exit(EXIT_FAILURE);
270281
}
271282
}
272283
} catch (std::exception const &e) {
273-
dout(1) << "Error: " << e.what() << dendl;
284+
derr << "Error: " << e.what() << dendl;
274285
exit(EXIT_FAILURE);
275286
}
276287
}

src/exporter/web_server.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
#include <string>
44

55
void web_server_thread_entrypoint();
6+
void stop_web_server();

0 commit comments

Comments
 (0)