]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
s3/smb_prometheus_endpoint: export per-share profile stats
authorShachar Sharon <ssharon@redhat.com>
Mon, 26 May 2025 08:51:04 +0000 (11:51 +0300)
committerAnoop C S <anoopcs@samba.org>
Mon, 23 Jun 2025 13:04:31 +0000 (13:04 +0000)
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 <ssharon@redhat.com>
Reviewed-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Anoop C S <anoopcs@samba.org>
source3/utils/smb_prometheus_endpoint.c

index 138fd53f8687653f2a10e399ab4b01dcca065ee7..43d68aafdab7c544b1e988d2abb490c68a5a2a64 100644 (file)
@@ -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<<i,
+                               1 << i,
                                val->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);
 }