From: Shachar Sharon Date: Mon, 26 May 2025 08:51:04 +0000 (+0300) Subject: s3/smb_prometheus_endpoint: export per-share profile stats X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=5869280bec92c3390d5e455ba54f24a2b4178e70;p=thirdparty%2Fsamba.git s3/smb_prometheus_endpoint: export per-share profile stats Iterate over per-share TDB entries (is exists) and export as Prometheus metrics via call-back function. Allow passing share-name as additional label to Prometheus metrics. By default, use empty label to follow OpenMetrics[1] conventions ("Empty label values SHOULD be treated as if the label was not present"). [1] https://github.com/prometheus/OpenMetrics/blob/main/specification/OpenMetrics.md Signed-off-by: Shachar Sharon Reviewed-by: Volker Lendecke Reviewed-by: Anoop C S --- diff --git a/source3/utils/smb_prometheus_endpoint.c b/source3/utils/smb_prometheus_endpoint.c index 138fd53f868..43d68aafdab 100644 --- a/source3/utils/smb_prometheus_endpoint.c +++ b/source3/utils/smb_prometheus_endpoint.c @@ -104,7 +104,8 @@ static void export_time(const char *name, } } -static void export_basic(const char *name, +static void export_basic(const char *svc, + const char *name, const struct smbprofile_stats_basic *val, struct export_state *state) { @@ -122,17 +123,19 @@ static void export_basic(const char *name, state->sent_help_smb1_request_total = true; } - evbuffer_add_printf( - state->buf, - "smb_smb1_request_total { operation=\"%s\" } " - "%" PRIu64 "\n", - name + 3, - val->count); + evbuffer_add_printf(state->buf, + "smb_smb1_request_total { " + "share=\"%s\",operation=\"%s\" } " + "%" PRIu64 "\n", + svc, + name + 3, + val->count); } return; } -static void export_iobytes_inbytes(const char *name, +static void export_iobytes_inbytes(const char *svc, + const char *name, const struct smbprofile_stats_iobytes *val, struct export_state *state) { @@ -149,18 +152,20 @@ static void export_iobytes_inbytes(const char *name, state->sent_help_smb2_request_inbytes = true; } - evbuffer_add_printf( - state->buf, - "smb_smb2_request_inbytes { operation=\"%s\" } " - "%" PRIu64 "\n", - name + 5, - val->inbytes); + evbuffer_add_printf(state->buf, + "smb_smb2_request_inbytes { " + "share=\"%s\",operation=\"%s\" } " + "%" PRIu64 "\n", + svc, + name + 5, + val->inbytes); } } -static void export_iobytes_outbytes(const char *name, - const struct smbprofile_stats_iobytes *val, - struct export_state *state) +static void export_iobytes_outbytes(const char *svc, + const char *name, + const struct smbprofile_stats_iobytes *val, + struct export_state *state) { bool is_smb2; @@ -175,16 +180,18 @@ static void export_iobytes_outbytes(const char *name, state->sent_help_smb2_request_outbytes = true; } - evbuffer_add_printf( - state->buf, - "smb_smb2_request_outbytes { operation=\"%s\" } " - "%" PRIu64 "\n", - name + 5, - val->outbytes); + evbuffer_add_printf(state->buf, + "smb_smb2_request_outbytes { " + "share=\"%s\",operation=\"%s\" } " + "%" PRIu64 "\n", + svc, + name + 5, + val->outbytes); } } -static void export_iobytes_buckets(const char *name, +static void export_iobytes_buckets(const char *svc, + const char *name, const struct smbprofile_stats_iobytes *val, struct export_state *state) { @@ -208,37 +215,42 @@ static void export_iobytes_buckets(const char *name, evbuffer_add_printf( state->buf, "smb_smb2_request_duration_microseconds_bucket " - "{operation=\"%s\",le=\"%d000\"} " + "{share=\"%s\",operation=\"%s\",le=\"%d000\"} " "%" PRIu64 "\n", + svc, name + 5, - 1<buckets[i]); } evbuffer_add_printf( state->buf, "smb_smb2_request_duration_microseconds_bucket " - "{operation=\"%s\",le=\"+Inf\"} " + "{share=\"%s\",operation=\"%s\",le=\"+Inf\"} " "%" PRIu64 "\n", + svc, name + 5, val->buckets[9]); evbuffer_add_printf( state->buf, "smb_smb2_request_duration_microseconds_sum " - "{operation=\"%s\"} " + "{share=\"%s\",operation=\"%s\"} " "%" PRIu64 "\n", + svc, name + 5, val->time); evbuffer_add_printf( state->buf, "smb_smb2_request_duration_microseconds_count " - "{operation=\"%s\"} " + "{share=\"%s\",operation=\"%s\"} " "%" PRIu64 "\n", + svc, name + 5, val->count); } } -static void export_iobytes_failed(const char *name, +static void export_iobytes_failed(const char *svc, + const char *name, const struct smbprofile_stats_iobytes *val, struct export_state *state) { @@ -255,116 +267,32 @@ static void export_iobytes_failed(const char *name, state->sent_help_smb2_request_failed = true; } - evbuffer_add_printf( - state->buf, - "smb_smb2_request_failed { operation=\"%s\" } " - "%" PRIu64 "\n", - name + 5, - val->failed_count); + evbuffer_add_printf(state->buf, + "smb_smb2_request_failed { " + "share=\"%s\",operation=\"%s\" } " + "%" PRIu64 "\n", + svc, + name + 5, + val->failed_count); } } -static void metrics_handler(struct evhttp_request *req, void *arg) +static void export_profile_stats(const struct profile_stats *stats, + struct export_state *state) { - struct export_state state = {.buf = NULL}; - const char *tdbfilename = arg; - struct tdb_context *tdb = NULL; - struct profile_stats stats = {.magic = 0}; - uint64_t magic; - size_t num_workers; - int ret; - - evhttp_add_header(req->output_headers, - "Content-Type", - "text/plain; charset=UTF-8"); - evhttp_add_header(req->output_headers, "Connection", "close"); - - state.buf = evbuffer_new(); - if (state.buf == NULL) { - evhttp_send_reply(req, HTTP_INTERNAL, "NOMEM", state.buf); - return; - } - - /* - * Open with O_RDWR although we won't write, but we want - * locking. - */ - tdb = tdb_open(tdbfilename, - 0, - TDB_CLEAR_IF_FIRST | TDB_MUTEX_LOCKING, - O_RDWR, - 0); - if (tdb == NULL) { - evbuffer_add_printf(state.buf, - "Could not open %s: %s\n", - tdbfilename, - strerror(errno)); - evhttp_send_reply(req, - HTTP_INTERNAL, - "TDB failure", - state.buf); - evbuffer_free(state.buf); - return; - } - - ret = smbprofile_magic(&stats, &magic); - if (ret != 0) { - evbuffer_add_printf(state.buf, "Could calculate magic"); - evhttp_send_reply(req, - HTTP_INTERNAL, - "magic failure", - state.buf); - evbuffer_free(state.buf); - return; - } - - num_workers = smbprofile_collect_tdb(tdb, magic, &stats); - - tdb_close(tdb); - - evbuffer_add_printf( - state.buf, - "# HELP smb_worker_smbd_num Number of worker smbds " - "serving clients\n" - "# TYPE smb_worker_smbd_num gauge\n" - "smb_worker_smbd_num %zu\n", - num_workers); - - evbuffer_add_printf( - state.buf, - "# HELP smb_num_authenticated_sessions Number of users " - "logged in\n" - "# TYPE smb_num_authenticated_sessions gauge\n" - "smb_num_authenticated_sessions %"PRIu64"\n", - stats.values.num_sessions_stats.count); - - evbuffer_add_printf( - state.buf, - "# HELP smb_num_tree_connects Number of share connections\n" - "# TYPE smb_num_tree_connects gauge\n" - "smb_num_tree_connects %"PRIu64"\n", - stats.values.num_tcons_stats.count); - - evbuffer_add_printf( - state.buf, - "# HELP smb_num_open_files Number of open files\n" - "# TYPE smb_num_open_files gauge\n" - "smb_num_open_files %"PRIu64"\n", - stats.values.num_files_stats.count); - #define SMBPROFILE_STATS_START #define SMBPROFILE_STATS_SECTION_START(name, display) #define SMBPROFILE_STATS_COUNT(name) \ do { \ - export_count(#name, &stats.values.name##_stats, &state); \ + export_count(#name, &stats->values.name##_stats, state); \ } while (0); #define SMBPROFILE_STATS_TIME(name) \ do { \ - export_time(#name, &stats.values.name##_stats, &state); \ + export_time(#name, &stats->values.name##_stats, state); \ } while (0); -#define SMBPROFILE_STATS_BASIC(name) \ - do { \ - export_basic(#name, &stats.values.name##_stats, &state); \ +#define SMBPROFILE_STATS_BASIC(name) \ + do { \ + export_basic("", #name, &stats->values.name##_stats, state); \ } while (0); #define SMBPROFILE_STATS_BYTES(name) #define SMBPROFILE_STATS_IOBYTES(name) @@ -387,10 +315,12 @@ static void metrics_handler(struct evhttp_request *req, void *arg) #define SMBPROFILE_STATS_TIME(name) #define SMBPROFILE_STATS_BASIC(name) #define SMBPROFILE_STATS_BYTES(name) -#define SMBPROFILE_STATS_IOBYTES(name) \ - do { \ - export_iobytes_inbytes( \ - #name, &stats.values.name##_stats, &state); \ +#define SMBPROFILE_STATS_IOBYTES(name) \ + do { \ + export_iobytes_inbytes("", \ + #name, \ + &stats->values.name##_stats, \ + state); \ } while (0); #define SMBPROFILE_STATS_SECTION_END #define SMBPROFILE_STATS_END @@ -411,10 +341,12 @@ static void metrics_handler(struct evhttp_request *req, void *arg) #define SMBPROFILE_STATS_TIME(name) #define SMBPROFILE_STATS_BASIC(name) #define SMBPROFILE_STATS_BYTES(name) -#define SMBPROFILE_STATS_IOBYTES(name) \ - do { \ - export_iobytes_outbytes( \ - #name, &stats.values.name##_stats, &state); \ +#define SMBPROFILE_STATS_IOBYTES(name) \ + do { \ + export_iobytes_outbytes("", \ + #name, \ + &stats->values.name##_stats, \ + state); \ } while (0); #define SMBPROFILE_STATS_SECTION_END #define SMBPROFILE_STATS_END @@ -435,10 +367,12 @@ static void metrics_handler(struct evhttp_request *req, void *arg) #define SMBPROFILE_STATS_TIME(name) #define SMBPROFILE_STATS_BASIC(name) #define SMBPROFILE_STATS_BYTES(name) -#define SMBPROFILE_STATS_IOBYTES(name) \ - do { \ - export_iobytes_buckets( \ - #name, &stats.values.name##_stats, &state); \ +#define SMBPROFILE_STATS_IOBYTES(name) \ + do { \ + export_iobytes_buckets("", \ + #name, \ + &stats->values.name##_stats, \ + state); \ } while (0); #define SMBPROFILE_STATS_SECTION_END #define SMBPROFILE_STATS_END @@ -459,11 +393,12 @@ static void metrics_handler(struct evhttp_request *req, void *arg) #define SMBPROFILE_STATS_TIME(name) #define SMBPROFILE_STATS_BASIC(name) #define SMBPROFILE_STATS_BYTES(name) -#define SMBPROFILE_STATS_IOBYTES(name) \ - do { \ - export_iobytes_failed(#name, \ - &stats.values.name##_stats, \ - &state); \ +#define SMBPROFILE_STATS_IOBYTES(name) \ + do { \ + export_iobytes_failed("", \ + #name, \ + &stats->values.name##_stats, \ + state); \ } while (0); #define SMBPROFILE_STATS_SECTION_END #define SMBPROFILE_STATS_END @@ -477,7 +412,260 @@ static void metrics_handler(struct evhttp_request *req, void *arg) #undef SMBPROFILE_STATS_IOBYTES #undef SMBPROFILE_STATS_SECTION_END #undef SMBPROFILE_STATS_END +} +static void export_profile_persvc_stats(const struct profile_stats *stats, + const char *svc, + struct export_state *state) +{ +#define SMBPROFILE_STATS_START +#define SMBPROFILE_STATS_SECTION_START(name, display) +#define SMBPROFILE_STATS_BASIC(name) \ + do { \ + export_basic(svc, #name, &stats->values.name##_stats, state); \ + } while (0); +#define SMBPROFILE_STATS_BYTES(name) +#define SMBPROFILE_STATS_IOBYTES(name) +#define SMBPROFILE_STATS_SECTION_END +#define SMBPROFILE_STATS_END + SMBPROFILE_STATS_PERSVC_SECTIONS +#undef SMBPROFILE_STATS_START +#undef SMBPROFILE_STATS_SECTION_START +#undef SMBPROFILE_STATS_COUNT +#undef SMBPROFILE_STATS_TIME +#undef SMBPROFILE_STATS_BASIC +#undef SMBPROFILE_STATS_BYTES +#undef SMBPROFILE_STATS_IOBYTES +#undef SMBPROFILE_STATS_SECTION_END +#undef SMBPROFILE_STATS_END + +#define SMBPROFILE_STATS_START +#define SMBPROFILE_STATS_SECTION_START(name, display) +#define SMBPROFILE_STATS_COUNT(name) +#define SMBPROFILE_STATS_TIME(name) +#define SMBPROFILE_STATS_BASIC(name) +#define SMBPROFILE_STATS_BYTES(name) +#define SMBPROFILE_STATS_IOBYTES(name) \ + do { \ + export_iobytes_inbytes(svc, \ + #name, \ + &stats->values.name##_stats, \ + state); \ + } while (0); +#define SMBPROFILE_STATS_SECTION_END +#define SMBPROFILE_STATS_END + SMBPROFILE_STATS_ALL_SECTIONS +#undef SMBPROFILE_STATS_START +#undef SMBPROFILE_STATS_SECTION_START +#undef SMBPROFILE_STATS_COUNT +#undef SMBPROFILE_STATS_TIME +#undef SMBPROFILE_STATS_BASIC +#undef SMBPROFILE_STATS_BYTES +#undef SMBPROFILE_STATS_IOBYTES +#undef SMBPROFILE_STATS_SECTION_END +#undef SMBPROFILE_STATS_END + +#define SMBPROFILE_STATS_START +#define SMBPROFILE_STATS_SECTION_START(name, display) +#define SMBPROFILE_STATS_COUNT(name) +#define SMBPROFILE_STATS_TIME(name) +#define SMBPROFILE_STATS_BASIC(name) +#define SMBPROFILE_STATS_BYTES(name) +#define SMBPROFILE_STATS_IOBYTES(name) \ + do { \ + export_iobytes_outbytes(svc, \ + #name, \ + &stats->values.name##_stats, \ + state); \ + } while (0); +#define SMBPROFILE_STATS_SECTION_END +#define SMBPROFILE_STATS_END + SMBPROFILE_STATS_PERSVC_SECTIONS +#undef SMBPROFILE_STATS_START +#undef SMBPROFILE_STATS_SECTION_START +#undef SMBPROFILE_STATS_COUNT +#undef SMBPROFILE_STATS_TIME +#undef SMBPROFILE_STATS_BASIC +#undef SMBPROFILE_STATS_BYTES +#undef SMBPROFILE_STATS_IOBYTES +#undef SMBPROFILE_STATS_SECTION_END +#undef SMBPROFILE_STATS_END + +#define SMBPROFILE_STATS_START +#define SMBPROFILE_STATS_SECTION_START(name, display) +#define SMBPROFILE_STATS_COUNT(name) +#define SMBPROFILE_STATS_TIME(name) +#define SMBPROFILE_STATS_BASIC(name) +#define SMBPROFILE_STATS_BYTES(name) +#define SMBPROFILE_STATS_IOBYTES(name) \ + do { \ + export_iobytes_buckets(svc, \ + #name, \ + &stats->values.name##_stats, \ + state); \ + } while (0); +#define SMBPROFILE_STATS_SECTION_END +#define SMBPROFILE_STATS_END + SMBPROFILE_STATS_PERSVC_SECTIONS +#undef SMBPROFILE_STATS_START +#undef SMBPROFILE_STATS_SECTION_START +#undef SMBPROFILE_STATS_COUNT +#undef SMBPROFILE_STATS_TIME +#undef SMBPROFILE_STATS_BASIC +#undef SMBPROFILE_STATS_BYTES +#undef SMBPROFILE_STATS_IOBYTES +#undef SMBPROFILE_STATS_SECTION_END +#undef SMBPROFILE_STATS_END + +#define SMBPROFILE_STATS_START +#define SMBPROFILE_STATS_SECTION_START(name, display) +#define SMBPROFILE_STATS_COUNT(name) +#define SMBPROFILE_STATS_TIME(name) +#define SMBPROFILE_STATS_BASIC(name) +#define SMBPROFILE_STATS_BYTES(name) +#define SMBPROFILE_STATS_IOBYTES(name) \ + do { \ + export_iobytes_failed(svc, \ + #name, \ + &stats->values.name##_stats, \ + state); \ + } while (0); +#define SMBPROFILE_STATS_SECTION_END +#define SMBPROFILE_STATS_END + SMBPROFILE_STATS_PERSVC_SECTIONS +#undef SMBPROFILE_STATS_START +#undef SMBPROFILE_STATS_SECTION_START +#undef SMBPROFILE_STATS_COUNT +#undef SMBPROFILE_STATS_TIME +#undef SMBPROFILE_STATS_BASIC +#undef SMBPROFILE_STATS_BYTES +#undef SMBPROFILE_STATS_IOBYTES +#undef SMBPROFILE_STATS_SECTION_END +#undef SMBPROFILE_STATS_END +} + +static int export_profile_persvc(const char *key, + const struct profile_stats *stats, + void *userp) +{ + struct export_state *state = userp; + char *svc = NULL; + char *sep = NULL; + + svc = strdup(key); + if (svc == NULL) { + return -1; + } + + /* convert dbkey into service name only */ + sep = strchr(svc, ':'); + if (sep != NULL) { + *sep = '\0'; + } + + export_profile_persvc_stats(stats, svc, state); + free(svc); + return 0; +} + +static void metrics_handler(struct evhttp_request *req, void *arg) +{ + struct export_state state = {.buf = NULL}; + const char *tdbfilename = arg; + struct tdb_context *tdb = NULL; + struct profile_stats stats = {.magic = 0}; + uint64_t magic; + size_t num_workers; + int ret; + + evhttp_add_header(req->output_headers, + "Content-Type", + "text/plain; charset=UTF-8"); + evhttp_add_header(req->output_headers, "Connection", "close"); + + state.buf = evbuffer_new(); + if (state.buf == NULL) { + evhttp_send_reply(req, HTTP_INTERNAL, "NOMEM", state.buf); + return; + } + + /* + * Open with O_RDWR although we won't write, but we want + * locking. + */ + tdb = tdb_open(tdbfilename, + 0, + TDB_CLEAR_IF_FIRST | TDB_MUTEX_LOCKING, + O_RDWR, + 0); + if (tdb == NULL) { + evbuffer_add_printf(state.buf, + "Could not open %s: %s\n", + tdbfilename, + strerror(errno)); + evhttp_send_reply(req, HTTP_INTERNAL, "TDB failure", state.buf); + evbuffer_free(state.buf); + return; + } + + ret = smbprofile_magic(&stats, &magic); + if (ret != 0) { + evbuffer_add_printf(state.buf, "Could calculate magic"); + evhttp_send_reply(req, + HTTP_INTERNAL, + "magic failure", + state.buf); + evbuffer_free(state.buf); + return; + } + + num_workers = smbprofile_collect_tdb(tdb, magic, &stats); + + tdb_close(tdb); + + evbuffer_add_printf(state.buf, + "# HELP smb_worker_smbd_num Number of worker smbds " + "serving clients\n" + "# TYPE smb_worker_smbd_num gauge\n" + "smb_worker_smbd_num %zu\n", + num_workers); + + evbuffer_add_printf( + state.buf, + "# HELP smb_num_authenticated_sessions Number of users " + "logged in\n" + "# TYPE smb_num_authenticated_sessions gauge\n" + "smb_num_authenticated_sessions %" PRIu64 "\n", + stats.values.num_sessions_stats.count); + + evbuffer_add_printf( + state.buf, + "# HELP smb_num_tree_connects Number of share connections\n" + "# TYPE smb_num_tree_connects gauge\n" + "smb_num_tree_connects %" PRIu64 "\n", + stats.values.num_tcons_stats.count); + + evbuffer_add_printf(state.buf, + "# HELP smb_num_open_files Number of open files\n" + "# TYPE smb_num_open_files gauge\n" + "smb_num_open_files %" PRIu64 "\n", + stats.values.num_files_stats.count); + + export_profile_stats(&stats, &state); + + /* + * Re-open TDB file with read-only mode and iterate-export per-share + * metrics. Ignore failure case this time. + */ + tdb = tdb_open(tdbfilename, 0, 0, O_RDONLY, 0); + if (tdb == NULL) { + goto out; + } + + smbprofile_persvc_collect_tdb(tdb, export_profile_persvc, &state); + + tdb_close(tdb); +out: evhttp_send_reply(req, HTTP_OK, "OK", state.buf); evbuffer_free(state.buf); }