From: Volker Lendecke Date: Fri, 17 Nov 2023 14:15:13 +0000 (+0100) Subject: utils: Initial version of smb_prometheus_endpoint X-Git-Tag: tevent-0.17.0~341 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=738e9d88ef94aa4db56e8ce2cbb368b22daba4bf;p=thirdparty%2Fsamba.git utils: Initial version of smb_prometheus_endpoint Signed-off-by: Volker Lendecke Reviewed-by: Ralph Boehme Reviewed-by: Guenther Deschner --- diff --git a/docs-xml/manpages/smb_prometheus_endpoint.8.xml b/docs-xml/manpages/smb_prometheus_endpoint.8.xml new file mode 100644 index 00000000000..cbfa71f4ffd --- /dev/null +++ b/docs-xml/manpages/smb_prometheus_endpoint.8.xml @@ -0,0 +1,75 @@ + + + + + + smb_prometheus_endpoint + 8 + Samba + System Administration tools + &doc.version; + + + + smb_prometheus_endpoint + Make Samba profiling information available to + prometheus. + + + + + smb_prometheus_endpoint + -a ADDRESS + -p PORT + smbprofile.tdb + + + + + DESCRIPTION + + This tool is part of the samba + 8 suite. + + Make the Samba profiling information available to prometheus via http. + + + + OPTIONS + + + -a ADDRESS + + The address which + smb_prometheus_endpoint shall listen + on, default is 127.0.0.1. + + + + -p PORT + + The port which + smb_prometheus_endpoint shall listen + on, default is port 9922. + + + + smbprofile.tdb + + The full path to the system's smbprofile.tdb where + Samba stores its statistics. + + + + + + + AUTHOR + + The original Samba software and related utilities + were created by Andrew Tridgell. Samba is now developed + by the Samba Team as an Open Source project similar + to the way the Linux kernel is developed. + + + diff --git a/docs-xml/wscript_build b/docs-xml/wscript_build index 967e18a6596..90f5d57ffc4 100644 --- a/docs-xml/wscript_build +++ b/docs-xml/wscript_build @@ -48,6 +48,7 @@ manpages=''' manpages/smbstatus.1 manpages/smbtar.1 manpages/smbtree.1 + manpages/smb_prometheus_endpoint.8 manpages/testparm.1 manpages/traffic_learner.7 manpages/traffic_replay.7 diff --git a/source3/utils/smb_prometheus_endpoint.c b/source3/utils/smb_prometheus_endpoint.c new file mode 100644 index 00000000000..2fa1da9c6b2 --- /dev/null +++ b/source3/utils/smb_prometheus_endpoint.c @@ -0,0 +1,471 @@ +/* + * Unix SMB/CIFS implementation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "replace.h" +#include "system/filesys.h" +#include +#include "source3/include/smbprofile.h" +#include "event2/event.h" +#include "event2/http.h" +#include "event2/http_struct.h" +#include "event2/buffer.h" + +struct export_state { + struct evbuffer *buf; + bool sent_help_cpu_seconds : 1; + bool sent_help_smb1_request_total : 1; + bool sent_help_smb2_request_total : 1; + bool sent_help_smb2_request_inbytes : 1; + bool sent_help_smb2_request_outbytes : 1; + bool sent_help_smb2_request_hist : 1; +}; + +static void export_count(const char *name, + const struct smbprofile_stats_count *val, + struct export_state *state) +{ + return; +} + +static void export_time(const char *name, + const struct smbprofile_stats_time *val, + struct export_state *state) +{ + bool is_cpu; + + is_cpu = (strncmp(name, "cpu_", 4) == 0); + if (is_cpu) { + const char *mode = name + 4; + + if (!state->sent_help_cpu_seconds) { + evbuffer_add_printf( + state->buf, + "# HELP smb_cpu_seconds_total Seconds spent " + "in worker smbds\n" + "# TYPE smb_cpu_seconds_total counter\n"); + state->sent_help_cpu_seconds = true; + } + + evbuffer_add_printf( + state->buf, + "smb_cpu_seconds_total { mode=\"%s\" } %f\n", + mode, + ((double)val->time) / 1000000); + } +} + +static void export_basic(const char *name, + const struct smbprofile_stats_basic *val, + struct export_state *state) +{ + bool is_smb; + + is_smb = (strncmp(name, "SMB", 3) == 0); + if (is_smb) { + + if (!state->sent_help_smb1_request_total) { + evbuffer_add_printf( + state->buf, + "# HELP smb_smb1_request_total Number of " + "SMB1 requests\n" + "# TYPE smb_smb1_request_total counter\n"); + state->sent_help_smb1_request_total = true; + } + + evbuffer_add_printf( + state->buf, + "smb_smb1_request_total { operation=\"%s\" } " + "%" PRIu64 "\n", + name + 3, + val->count); + } + return; +} + +static void export_iobytes_inbytes(const char *name, + const struct smbprofile_stats_iobytes *val, + struct export_state *state) +{ + bool is_smb2; + + is_smb2 = (strncmp(name, "smb2_", 5) == 0); + if (is_smb2) { + if (!state->sent_help_smb2_request_inbytes) { + evbuffer_add_printf( + state->buf, + "# HELP smb_smb2_request_inbytes Bytes " + "received for SMB2 requests\n" + "# TYPE smb_smb2_request_inbytes counter\n"); + state->sent_help_smb2_request_inbytes = true; + } + + evbuffer_add_printf( + state->buf, + "smb_smb2_request_inbytes { operation=\"%s\" } " + "%" PRIu64 "\n", + name + 5, + val->inbytes); + } +} + +static void export_iobytes_outbytes(const char *name, + const struct smbprofile_stats_iobytes *val, + struct export_state *state) +{ + bool is_smb2; + + is_smb2 = (strncmp(name, "smb2_", 5) == 0); + if (is_smb2) { + if (!state->sent_help_smb2_request_outbytes) { + evbuffer_add_printf( + state->buf, + "# HELP smb_smb2_request_outbytes Bytes " + "received for SMB2 requests\n" + "# TYPE smb_smb2_request_outbytes counter\n"); + state->sent_help_smb2_request_outbytes = true; + } + + evbuffer_add_printf( + state->buf, + "smb_smb2_request_outbytes { operation=\"%s\" } " + "%" PRIu64 "\n", + name + 5, + val->outbytes); + } +} + +static void export_iobytes_buckets(const char *name, + const struct smbprofile_stats_iobytes *val, + struct export_state *state) +{ + bool is_smb2; + + is_smb2 = (strncmp(name, "smb2_", 5) == 0); + if (is_smb2) { + int i; + + if (!state->sent_help_smb2_request_hist) { + evbuffer_add_printf( + state->buf, + "# HELP smb_smb2_request_duration_microseconds " + "Histogram of latencies for SMB2 requests\n" + "# TYPE smb_smb2_request_duration_microseconds " + "histogram\n"); + state->sent_help_smb2_request_hist = true; + } + + for (i=0; i<9; i++) { + evbuffer_add_printf( + state->buf, + "smb_smb2_request_duration_microseconds_bucket " + "{operation=\"%s\",le=\"%d000\"} " + "%" PRIu64 "\n", + name + 5, + 1<buckets[i]); + } + evbuffer_add_printf( + state->buf, + "smb_smb2_request_duration_microseconds_bucket " + "{operation=\"%s\",le=\"+Inf\"} " + "%" PRIu64 "\n", + name + 5, + val->buckets[9]); + evbuffer_add_printf( + state->buf, + "smb_smb2_request_duration_microseconds_sum " + "{operation=\"%s\"} " + "%" PRIu64 "\n", + name + 5, + val->time); + evbuffer_add_printf( + state->buf, + "smb_smb2_request_duration_microseconds_count " + "{operation=\"%s\"} " + "%" PRIu64 "\n", + name + 5, + val->count); + } +} + +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); + +#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); \ + } while (0); +#define SMBPROFILE_STATS_TIME(name) \ + do { \ + export_time(#name, &stats.values.name##_stats, &state); \ + } while (0); +#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) +#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_inbytes( \ + #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( \ + #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_buckets( \ + #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 + + evhttp_send_reply(req, HTTP_OK, "OK", state.buf); + evbuffer_free(state.buf); +} + +static void default_handler(struct evhttp_request *req, void *arg) +{ + struct evbuffer *buf = NULL; + + evhttp_add_header(req->output_headers, + "Content-Type", + "text/plain; charset=UTF-8"); + evhttp_add_header(req->output_headers, "Connection", "close"); + + buf = evbuffer_new(); + if (buf != NULL) { + evbuffer_add_printf(buf, "404 Not Found\n"); + } + + evhttp_send_reply(req, HTTP_NOTFOUND, "OK", buf); + + if (buf != NULL) { + evbuffer_free(buf); + } +} + +int main(int argc, char *argv[]) +{ + struct event_base *ev = NULL; + struct evhttp *http_server = NULL; + char *tdbfilename = NULL; + const char *addr = "127.0.0.1"; + uint16_t port = 9922; + int ret, c; + + while ((c = getopt(argc, argv, "a:p:")) != -1) { + switch (c) { + case 'a': + addr = optarg; + break; + case 'p': + port = atoi(optarg); + break; + } + } + + if (optind != argc - 1) { + fprintf(stderr, "Missing tdb filename\n"); + return 1; + } + tdbfilename = argv[optind]; + + ev = event_base_new(); + if (ev == NULL) { + fprintf(stderr, "event_base_new() failed\n"); + return 1; + } + + http_server = evhttp_new(ev); + if (http_server == NULL) { + fprintf(stderr, "evhttp_new() failed\n"); + return 1; + } + + ret = evhttp_bind_socket(http_server, addr, port); + if (ret != 0) { + fprintf(stderr, "evhttp_bind_socket failed\n"); + return 1; + } + + evhttp_set_gencb(http_server, default_handler, ev); + evhttp_set_cb(http_server, "/metrics", metrics_handler, tdbfilename); + event_base_dispatch(ev); + + evhttp_free(http_server); + event_base_free(ev); + + return 0; +} diff --git a/source3/utils/wscript_build b/source3/utils/wscript_build index 85deb74eab6..007ec63c2af 100644 --- a/source3/utils/wscript_build +++ b/source3/utils/wscript_build @@ -323,6 +323,16 @@ bld.SAMBA3_BINARY('smbstatus', CONN_TDB ''') +bld.SAMBA_BINARY('smb_prometheus_endpoint', + source='smb_prometheus_endpoint.c', + deps=''' + replace + event + PROFILE_READ + ''', + install_path='${SBINDIR}', + enabled=bld.CONFIG_SET("HAVE_EVHTTP_NEW") and bld.CONFIG_GET("WITH_PROFILE")) + bld.SAMBA3_BINARY('mdsearch', source='mdsearch.c', deps='''