// SPDX-License-Identifier: GPL-3.0-or-later
#define EXPORTINGS_INTERNALS
#include "prometheus.h"
// ----------------------------------------------------------------------------
// PROMETHEUS
// /api/v1/allmetrics?format=prometheus and /api/v1/allmetrics?format=prometheus_all_hosts
/**
* Check if a chart can be sent to Prometheus
*
* @param instance an instance data structure.
* @param st a chart.
* @return Returns 1 if the chart can be sent, 0 otherwise.
*/
inline int can_send_rrdset(struct instance *instance, RRDSET *st)
{
RRDHOST *host = st->rrdhost;
if (unlikely(rrdset_flag_check(st, RRDSET_FLAG_BACKEND_IGNORE)))
return 0;
if (unlikely(!rrdset_flag_check(st, RRDSET_FLAG_BACKEND_SEND))) {
// we have not checked this chart
if (simple_pattern_matches(instance->config.charts_pattern, st->id) ||
simple_pattern_matches(instance->config.charts_pattern, st->name))
rrdset_flag_set(st, RRDSET_FLAG_BACKEND_SEND);
else {
rrdset_flag_set(st, RRDSET_FLAG_BACKEND_IGNORE);
debug(
D_BACKEND,
"EXPORTING: not sending chart '%s' of host '%s', because it is disabled for exporting.",
st->id,
host->hostname);
return 0;
}
}
if (unlikely(!rrdset_is_available_for_backends(st))) {
debug(
D_BACKEND,
"EXPORTING: not sending chart '%s' of host '%s', because it is not available for exporting.",
st->id,
host->hostname);
return 0;
}
if (unlikely(
st->rrd_memory_mode == RRD_MEMORY_MODE_NONE &&
!(EXPORTING_OPTIONS_DATA_SOURCE(instance->config.options) == EXPORTING_SOURCE_DATA_AS_COLLECTED))) {
debug(
D_BACKEND,
"EXPORTING: not sending chart '%s' of host '%s' because its memory mode is '%s' and the exporting connector requires database access.",
st->id,
host->hostname,
rrd_memory_mode_name(host->rrd_memory_mode));
return 0;
}
return 1;
}
static struct prometheus_server {
const char *server;
uint32_t hash;
RRDHOST *host;
time_t last_access;
struct prometheus_server *next;
} *prometheus_server_root = NULL;
static netdata_mutex_t prometheus_server_root_mutex = NETDATA_MUTEX_INITIALIZER;
/**
* Clean server root local structure
*/
void prometheus_clean_server_root()
{
if (prometheus_server_root) {
netdata_mutex_lock(&prometheus_server_root_mutex);
struct prometheus_server *ps;
for (ps = prometheus_server_root; ps; ) {
struct prometheus_server *current = ps;
ps = ps->next;
if(current->server)
freez((void *)current->server);
freez(current);
}
prometheus_server_root = NULL;
netdata_mutex_unlock(&prometheus_server_root_mutex);
}
}
/**
* Get the last time when a Prometheus server scraped the Netdata Prometheus exporter.
*
* @param server the name of the Prometheus server.
* @param host a data collecting host.
* @param now actual time.
* @return Returns the last time when the server accessed Netdata, or 0 if it is the first occurrence.
*/
static inline time_t prometheus_server_last_access(const char *server, RRDHOST *host, time_t now)
{
#ifdef UNIT_TESTING
return 0;
#endif
uint32_t hash = simple_hash(server);
netdata_mutex_lock(&prometheus_server_root_mutex);
struct prometheus_server *ps;
for (ps = prometheus_server_root; ps; ps = ps->next) {
if (host == ps->host && hash == ps->hash && !strcmp(server, ps->server)) {
time_t last = ps->last_access;
ps->last_access = now;
netdata_mutex_unlock(&prometheus_server_root_mutex);
return last;
}
}
ps = callocz(1, sizeof(struct prometheus_server));
ps->server = strdupz(server);
ps->hash = hash;
ps->host = host;
ps->last_access = now;
ps->next = prometheus_server_root;
prometheus_server_root = ps;
netdata_mutex_unlock(&prometheus_server_root_mutex);
return 0;
}
/**
* Copy and sanitize name.
*
* @param d a destination string.
* @param s a source sting.
* @param usable the number of characters to copy.
* @return Returns the length of the copied string.
*/
inline size_t prometheus_name_copy(char *d, const char *s, size_t usable)
{
size_t n;
for (n = 0; *s && n < usable; d++, s++, n++) {
register char c = *s;
if (!isalnum(c))
<