<p>Friendly Automation <strong>merged</strong> this change.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/11337">View Change</a></p><div style="white-space:pre-wrap">Approvals:
George Joseph: Looks good to me, but someone else must approve
Matt Jordan: Looks good to me, approved
Friendly Automation: Approved for Submit
</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">Add core Prometheus support to Asterisk<br><br>Prometheus is the defacto monitoring tool for containerized applications.<br>This patch adds native support to Asterisk for serving up Prometheus<br>compatible metrics, such that a Prometheus server can scrape an Asterisk<br>instance in the same fashion as it does other HTTP services.<br><br>The core module in this patch provides an API that future work can build<br>on top of. The API manages metrics in one of two ways:<br>(1) Registered metrics. In this particular case, the API assumes that<br> the metric (either allocated on the stack or on the heap) will have<br> its value updated by the module registering it at will, and not<br> just when Prometheus scrapes Asterisk. When a scrape does occur,<br> the metrics are locked so that the current value can be retrieved.<br>(2) Scrape callbacks. In this case, the API allows consumers to be<br> called via a callback function when a Prometheus initiated scrape<br> occurs. The consumers of the API are responsible for populating<br> the response to Prometheus themselves, typically using stack<br> allocated metrics that are then formatted properly into strings<br> via this module's convenience functions.<br><br>These two mechanisms balance the different ways in which information is<br>generated within Asterisk: some information is generated in a fashion<br>that makes it appropriate to update the relevant metrics immediately;<br>some information is better to defer until a Prometheus server asks for<br>it.<br><br>Note that some care has been taken in how metrics are defined to<br>minimize the impact on performance. Prometheus's metric definition<br>and its support for nesting metrics based on labels - which are<br>effectively key/value pairs - can make storage and managing of metrics<br>somewhat tricky. While a naive approach, where we allow for any number<br>of labels and perform a lot of heap allocations to manage the information,<br>would absolutely have worked, this patch instead opts to try to place<br>as much information in length limited arrays, stack allocations, and<br>vectors to minimize the performance impacts of scrapes. The author of<br>this patch has worked on enough systems that were driven to their knees<br>by poor monitoring implementations to be a bit cautious.<br><br>Additionally, this patch only adds support for gauges and counters.<br>Additional work to add summaries, histograms, and other Prometheus<br>metric types may add value in the future. This would be of particular<br>interest if someone wanted to track SIP response types.<br><br>Finally, this patch includes unit tests for the core APIs.<br><br>ASTERISK-28403<br><br>Change-Id: I891433a272c92fd11c705a2c36d65479a415ec42<br>---<br>A configs/samples/prometheus.conf.sample<br>A include/asterisk/res_prometheus.h<br>A res/res_prometheus.c<br>A res/res_prometheus.exports.in<br>A tests/test_res_prometheus.c<br>5 files changed, 2,273 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/configs/samples/prometheus.conf.sample b/configs/samples/prometheus.conf.sample</span><br><span>new file mode 100644</span><br><span>index 0000000..63e9bd6</span><br><span>--- /dev/null</span><br><span>+++ b/configs/samples/prometheus.conf.sample</span><br><span>@@ -0,0 +1,61 @@</span><br><span style="color: hsl(120, 100%, 40%);">+;</span><br><span style="color: hsl(120, 100%, 40%);">+; res_prometheus Module configuration for Asterisk</span><br><span style="color: hsl(120, 100%, 40%);">+;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+;</span><br><span style="color: hsl(120, 100%, 40%);">+; Note that this configuration file is consumed by res_prometheus, which</span><br><span style="color: hsl(120, 100%, 40%);">+; provides core functionality for serving up Asterisk statistics to a</span><br><span style="color: hsl(120, 100%, 40%);">+; Prometheus server. By default, this only includes basic information about</span><br><span style="color: hsl(120, 100%, 40%);">+; the Asterisk instance that is running. Additional modules can be loaded to</span><br><span style="color: hsl(120, 100%, 40%);">+; provide specific statistics. In all cases, configuration of said statistics</span><br><span style="color: hsl(120, 100%, 40%);">+; is done through this configuration file.</span><br><span style="color: hsl(120, 100%, 40%);">+;</span><br><span style="color: hsl(120, 100%, 40%);">+; Because Prometheus scrapes statistics from HTTP servers, this module requires</span><br><span style="color: hsl(120, 100%, 40%);">+; Asterisk's built-in HTTP server to be enabled and configured properly.</span><br><span style="color: hsl(120, 100%, 40%);">+;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+; Settings that affect all statistic generation</span><br><span style="color: hsl(120, 100%, 40%);">+[general]</span><br><span style="color: hsl(120, 100%, 40%);">+enabled = no ; Enable/disable all statistic generation.</span><br><span style="color: hsl(120, 100%, 40%);">+ ; Default is "no", as enabling this without</span><br><span style="color: hsl(120, 100%, 40%);">+ ; proper securing of your Asterisk system</span><br><span style="color: hsl(120, 100%, 40%);">+ ; may result in external systems learning</span><br><span style="color: hsl(120, 100%, 40%);">+ ; a lot about your Asterisk system.</span><br><span style="color: hsl(120, 100%, 40%);">+ ; Note #1: If Asterisk's HTTP server is</span><br><span style="color: hsl(120, 100%, 40%);">+ ; disabled, this setting won't matter.</span><br><span style="color: hsl(120, 100%, 40%);">+ ; Note #2: It is highly recommended that you</span><br><span style="color: hsl(120, 100%, 40%);">+ ; set up Basic Auth and configure your</span><br><span style="color: hsl(120, 100%, 40%);">+ ; Prometheus server to authenticate with</span><br><span style="color: hsl(120, 100%, 40%);">+ ; Asterisk. Failing to do so will make it easy</span><br><span style="color: hsl(120, 100%, 40%);">+ ; for external systems to scrape your Asterisk</span><br><span style="color: hsl(120, 100%, 40%);">+ ; instance and learn things about your system</span><br><span style="color: hsl(120, 100%, 40%);">+ ; that you may not want them to. While the</span><br><span style="color: hsl(120, 100%, 40%);">+ ; metrics exposed by this module do not</span><br><span style="color: hsl(120, 100%, 40%);">+ ; necessarily contain information that can</span><br><span style="color: hsl(120, 100%, 40%);">+ ; lead to an exploit, an ounce of prevention</span><br><span style="color: hsl(120, 100%, 40%);">+ ; goes a long way. Particularly for those out</span><br><span style="color: hsl(120, 100%, 40%);">+ ; there who are exceedingly lax in updating</span><br><span style="color: hsl(120, 100%, 40%);">+ ; your Asterisk system. You are updating on a</span><br><span style="color: hsl(120, 100%, 40%);">+ ; regular cadence, aren't you???</span><br><span style="color: hsl(120, 100%, 40%);">+core_metrics_enabled = yes ; Enable/disable core metrics. Core metrics</span><br><span style="color: hsl(120, 100%, 40%);">+ ; include various properties such as the</span><br><span style="color: hsl(120, 100%, 40%);">+ ; version of Asterisk, uptime, last reload</span><br><span style="color: hsl(120, 100%, 40%);">+ ; time, and the overall time it takes to</span><br><span style="color: hsl(120, 100%, 40%);">+ ; scrape metrics. Default is "yes"</span><br><span style="color: hsl(120, 100%, 40%);">+uri = metrics ; The HTTP route to expose metrics on.</span><br><span style="color: hsl(120, 100%, 40%);">+ ; Default is "metrics".</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+; auth_username = Asterisk ; If provided, Basic Auth will be enabled on</span><br><span style="color: hsl(120, 100%, 40%);">+ ; the metrics route. Failure to provide both</span><br><span style="color: hsl(120, 100%, 40%);">+ ; auth_username and auth_password will result</span><br><span style="color: hsl(120, 100%, 40%);">+ ; in a module load error.</span><br><span style="color: hsl(120, 100%, 40%);">+; auth_password = ; The password to use for Basic Auth. Note</span><br><span style="color: hsl(120, 100%, 40%);">+ ; that I'm leaving this blank to prevent</span><br><span style="color: hsl(120, 100%, 40%);">+ ; you from merely uncommenting the line and</span><br><span style="color: hsl(120, 100%, 40%);">+ ; running with a config provided password.</span><br><span style="color: hsl(120, 100%, 40%);">+ ; Because yes, people actually *do* that.</span><br><span style="color: hsl(120, 100%, 40%);">+ ; I mean, if you're going to do that, just</span><br><span style="color: hsl(120, 100%, 40%);">+ ; run unsecured. Fake security is usually</span><br><span style="color: hsl(120, 100%, 40%);">+ ; worse than no security.</span><br><span style="color: hsl(120, 100%, 40%);">+; auth_realm = ; Realm to use for authentication. Defaults</span><br><span style="color: hsl(120, 100%, 40%);">+ ; to Asterisk Prometheus Metrics</span><br><span>diff --git a/include/asterisk/res_prometheus.h b/include/asterisk/res_prometheus.h</span><br><span>new file mode 100644</span><br><span>index 0000000..cf62b7b</span><br><span>--- /dev/null</span><br><span>+++ b/include/asterisk/res_prometheus.h</span><br><span>@@ -0,0 +1,478 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * res_prometheus: Asterisk Prometheus Metrics</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2019 Sangoma, Inc.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Matt Jordan <mjordan@digium.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifndef RES_PROMETHEUS_H__</span><br><span style="color: hsl(120, 100%, 40%);">+#define RES_PROMETHEUS_H__</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \file res_prometheus</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Asterisk Prometheus Metrics</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This module provides the base APIs and functionality for exposing a</span><br><span style="color: hsl(120, 100%, 40%);">+ * metrics route in Asterisk's HTTP server suitable for consumption by</span><br><span style="color: hsl(120, 100%, 40%);">+ * a Prometheus server. It does not provide any metrics itself.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/lock.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/linkedlists.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/stringfields.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief How many labels a single metric can have</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define PROMETHEUS_MAX_LABELS 8</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief How long a label name can be</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define PROMETHEUS_MAX_NAME_LENGTH 64</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief How long a label value can be</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define PROMETHEUS_MAX_LABEL_LENGTH 128</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief How large of a value we can store</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define PROMETHEUS_MAX_VALUE_LENGTH 32</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/**</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Prometheus general configuration</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \details</span><br><span style="color: hsl(120, 100%, 40%);">+ * While the config file should generally provide the configuration</span><br><span style="color: hsl(120, 100%, 40%);">+ * for this module, it is useful for testing purposes to allow the</span><br><span style="color: hsl(120, 100%, 40%);">+ * configuration to be injected into the module. This struct is</span><br><span style="color: hsl(120, 100%, 40%);">+ * public to allow this to occur.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note</span><br><span style="color: hsl(120, 100%, 40%);">+ * Modifying the configuration outside of testing purposes is not</span><br><span style="color: hsl(120, 100%, 40%);">+ * encouraged.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct prometheus_general_config {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \brief Whether or not the module is enabled */</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned int enabled;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \brief Whether or not core metrics are enabled */</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned int core_metrics_enabled;</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_DECLARE_STRING_FIELDS(</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \brief The HTTP URI we register ourselves to */</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_STRING_FIELD(uri);</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \brief Auth username for Basic Auth */</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_STRING_FIELD(auth_username);</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \brief Auth password for Basic Auth */</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_STRING_FIELD(auth_password);</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \brief Auth realm */</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_STRING_FIELD(auth_realm);</span><br><span style="color: hsl(120, 100%, 40%);">+ );</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Prometheus metric type</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note</span><br><span style="color: hsl(120, 100%, 40%);">+ * Clearly, at some point, we should support summaries and histograms.</span><br><span style="color: hsl(120, 100%, 40%);">+ * As an initial implementation, counters / gauges give us quite a</span><br><span style="color: hsl(120, 100%, 40%);">+ * bit of functionality.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+enum prometheus_metric_type {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief A metric whose value always goes up</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_COUNTER = 0,</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief A metric whose value can bounce around like a jackrabbit</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_GAUGE,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief How the metric was allocated.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note Clearly, you don't want to get this wrong.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+enum prometheus_metric_allocation_strategy {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief The metric was allocated on the stack</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_ALLOCD = 0,</span><br><span style="color: hsl(120, 100%, 40%);">+ /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief The metric was allocated on the heap</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_MALLOCD,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief A label that further defines a metric</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct prometheus_label {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief The name of the label</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ char name[PROMETHEUS_MAX_NAME_LENGTH];</span><br><span style="color: hsl(120, 100%, 40%);">+ /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief The value of the label</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ char value[PROMETHEUS_MAX_LABEL_LENGTH];</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief An actual, honest to god, metric.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \details</span><br><span style="color: hsl(120, 100%, 40%);">+ * A bit of effort has gone into making this structure as efficient as we</span><br><span style="color: hsl(120, 100%, 40%);">+ * possibly can. Given that a *lot* of metrics can theoretically be dumped out,</span><br><span style="color: hsl(120, 100%, 40%);">+ * and that Asterisk attempts to be a "real-time" system, we want this process</span><br><span style="color: hsl(120, 100%, 40%);">+ * to be as efficient as possible. Countering that is the ridiculous flexibility</span><br><span style="color: hsl(120, 100%, 40%);">+ * that Prometheus allows for (and, to an extent, wants) - namely the notion of</span><br><span style="color: hsl(120, 100%, 40%);">+ * families of metrics delineated by their labels.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * In order to balance this, metrics have arrays of labels. While this makes for</span><br><span style="color: hsl(120, 100%, 40%);">+ * a very large struct (such that loading one of these into memory is probably</span><br><span style="color: hsl(120, 100%, 40%);">+ * going to blow your cache), you will at least get the whole thing, since</span><br><span style="color: hsl(120, 100%, 40%);">+ * you're going to need those labels to figure out what you're looking like.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * A hierarchy of metrics occurs when all metrics have the same \c name, but</span><br><span style="color: hsl(120, 100%, 40%);">+ * different labels.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * We manage the hierarchy by allowing a metric to maintain their own list of</span><br><span style="color: hsl(120, 100%, 40%);">+ * related metrics. When metrics are registered (/c prometheus_metric_register),</span><br><span style="color: hsl(120, 100%, 40%);">+ * the function will automatically determine the hierarchy and place them into</span><br><span style="color: hsl(120, 100%, 40%);">+ * the appropriate lists. When you are creating metrics on the fly in a callback</span><br><span style="color: hsl(120, 100%, 40%);">+ * (\c prometheus_callback_register), you have to manage this hierarchy</span><br><span style="color: hsl(120, 100%, 40%);">+ * yourself, and only print out the first metric in a chain.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Note that **EVERYTHING** in a metric is immutable once registered, save for</span><br><span style="color: hsl(120, 100%, 40%);">+ * its value. Modifying the hierarchy, labels, name, help, whatever is going to</span><br><span style="color: hsl(120, 100%, 40%);">+ * result in a "bad time", and is also expressly against Prometheus law. (Don't</span><br><span style="color: hsl(120, 100%, 40%);">+ * get your liver eaten.)</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct prometheus_metric {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief What type of metric we are</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ enum prometheus_metric_type type;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief How this metric was allocated</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ enum prometheus_metric_allocation_strategy allocation_strategy;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief A lock protecting the metric \c value</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note The metric must be locked prior to updating its value!</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_mutex_t lock;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Pointer to a static string defining this metric's help text.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *help;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Our metric name</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ char name[PROMETHEUS_MAX_NAME_LENGTH];</span><br><span style="color: hsl(120, 100%, 40%);">+ /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief The metric's labels</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_label labels[PROMETHEUS_MAX_LABELS];</span><br><span style="color: hsl(120, 100%, 40%);">+ /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief The current value.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \details</span><br><span style="color: hsl(120, 100%, 40%);">+ * If \c get_metric_value is set, this value is ignored until the callback</span><br><span style="color: hsl(120, 100%, 40%);">+ * happens</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ char value[PROMETHEUS_MAX_VALUE_LENGTH];</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Callback function to obtain the metric value</span><br><span style="color: hsl(120, 100%, 40%);">+ * \details</span><br><span style="color: hsl(120, 100%, 40%);">+ * If updates need to happen when the metric is gathered, provide the</span><br><span style="color: hsl(120, 100%, 40%);">+ * callback function. Otherwise, leave it \c NULL.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ void (* get_metric_value)(struct prometheus_metric *metric);</span><br><span style="color: hsl(120, 100%, 40%);">+ /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief A list of children metrics</span><br><span style="color: hsl(120, 100%, 40%);">+ * \details</span><br><span style="color: hsl(120, 100%, 40%);">+ * Children metrics have the same name but different label.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Registration of a metric will automatically nest the metrics; otherwise</span><br><span style="color: hsl(120, 100%, 40%);">+ * they are treated independently.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * The help of the first metric in a chain of related metrics is the only</span><br><span style="color: hsl(120, 100%, 40%);">+ * one that will be printed.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * For metrics output during a callback, the handler is responsible for</span><br><span style="color: hsl(120, 100%, 40%);">+ * managing the children. For metrics that are registered, the registration</span><br><span style="color: hsl(120, 100%, 40%);">+ * automatically nests the metrics.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_LIST_HEAD_NOLOCK(, prometheus_metric) children;</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_LIST_ENTRY(prometheus_metric) entry;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/**</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Convenience macro for initializing a metric on the stack</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param mtype The metric type. See \c prometheus_metric_type</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param n Name of the metric</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param h Help text for the metric</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param cb Callback function. Optional; may be \c NULL</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \details</span><br><span style="color: hsl(120, 100%, 40%);">+ * When initializing a metric on the stack, various fields have to be provided</span><br><span style="color: hsl(120, 100%, 40%);">+ * to initialize the metric correctly. This macro can be used to simplify the</span><br><span style="color: hsl(120, 100%, 40%);">+ * process.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Example Usage:</span><br><span style="color: hsl(120, 100%, 40%);">+ * \code</span><br><span style="color: hsl(120, 100%, 40%);">+ * struct prometheus_metric test_counter_one =</span><br><span style="color: hsl(120, 100%, 40%);">+ * PROMETHEUS_METRIC_STATIC_INITIALIZATION(</span><br><span style="color: hsl(120, 100%, 40%);">+ * PROMETHEUS_METRIC_COUNTER,</span><br><span style="color: hsl(120, 100%, 40%);">+ * "test_counter_one",</span><br><span style="color: hsl(120, 100%, 40%);">+ * "A test counter",</span><br><span style="color: hsl(120, 100%, 40%);">+ * NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ * struct prometheus_metric test_counter_two =</span><br><span style="color: hsl(120, 100%, 40%);">+ * PROMETHEUS_METRIC_STATIC_INITIALIZATION(</span><br><span style="color: hsl(120, 100%, 40%);">+ * PROMETHEUS_METRIC_COUNTER,</span><br><span style="color: hsl(120, 100%, 40%);">+ * "test_counter_two",</span><br><span style="color: hsl(120, 100%, 40%);">+ * "A test counter",</span><br><span style="color: hsl(120, 100%, 40%);">+ * metric_values_get_counter_value_cb);</span><br><span style="color: hsl(120, 100%, 40%);">+ * \endcode</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define PROMETHEUS_METRIC_STATIC_INITIALIZATION(mtype, n, h, cb) { \</span><br><span style="color: hsl(120, 100%, 40%);">+ .type = (mtype), \</span><br><span style="color: hsl(120, 100%, 40%);">+ .allocation_strategy = PROMETHEUS_METRIC_ALLOCD, \</span><br><span style="color: hsl(120, 100%, 40%);">+ .lock = AST_MUTEX_INIT_VALUE, \</span><br><span style="color: hsl(120, 100%, 40%);">+ .name = (n), \</span><br><span style="color: hsl(120, 100%, 40%);">+ .help = (h), \</span><br><span style="color: hsl(120, 100%, 40%);">+ .children = AST_LIST_HEAD_NOLOCK_INIT_VALUE, \</span><br><span style="color: hsl(120, 100%, 40%);">+ .get_metric_value = (cb), \</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/**</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Convenience macro for setting a label / value in a metric</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param metric The metric to set the label on</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param label Position of the label to set</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param n Name of the label</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param v Value of the label</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \details</span><br><span style="color: hsl(120, 100%, 40%);">+ * When creating nested metrics, it's helpful to set their label after they have</span><br><span style="color: hsl(120, 100%, 40%);">+ * been declared but before they have been registered. This macro acts as a</span><br><span style="color: hsl(120, 100%, 40%);">+ * convenience function to set the labels properly on a declared metric.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note Setting labels *after* registration will lead to a "bad time"</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Example Usage:</span><br><span style="color: hsl(120, 100%, 40%);">+ * \code</span><br><span style="color: hsl(120, 100%, 40%);">+ * PROMETHEUS_METRIC_SET_LABEL(</span><br><span style="color: hsl(120, 100%, 40%);">+ * test_gauge_child_two, 0, "key_one", "value_two");</span><br><span style="color: hsl(120, 100%, 40%);">+ * PROMETHEUS_METRIC_SET_LABEL(</span><br><span style="color: hsl(120, 100%, 40%);">+ * test_gauge_child_two, 1, "key_two", "value_two");</span><br><span style="color: hsl(120, 100%, 40%);">+ * \endcode</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define PROMETHEUS_METRIC_SET_LABEL(metric, label, n, v) do { \</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_assert((label) < PROMETHEUS_MAX_LABELS); \</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_copy_string((metric)->labels[(label)].name, (n), sizeof((metric)->labels[(label)].name)); \</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_copy_string((metric)->labels[(label)].value, (v), sizeof((metric)->labels[(label)].value)); \</span><br><span style="color: hsl(120, 100%, 40%);">+} while (0)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Destroy a metric and all its children</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note If you still want the children, make sure you remove the head of the</span><br><span style="color: hsl(120, 100%, 40%);">+ * \c children list first.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param metric The metric to destroy</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void prometheus_metric_free(struct prometheus_metric *metric);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Create a malloc'd counter metric</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note The metric must be registered after creation</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param name The name of the metric</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param help Help text for the metric</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval prometheus_metric on success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval NULL on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct prometheus_metric *prometheus_counter_create(const char *name,</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *help);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Create a malloc'd gauge metric</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note The metric must be registered after creation</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param name The name of the metric</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param help Help text for the metric</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval prometheus_metric on success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval NULL on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct prometheus_metric *prometheus_gauge_create(const char *name,</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *help);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/**</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Convert a metric (and its children) into Prometheus compatible text</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param metric The metric to convert to a string</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param [out] output The \c ast_str string to populate with the metric(s)</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void prometheus_metric_to_string(struct prometheus_metric *metric,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str **output);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Defines a callback that will be invoked when the HTTP route is called</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \details</span><br><span style="color: hsl(120, 100%, 40%);">+ * This callback presents the second way of passing metrics to a Prometheus</span><br><span style="color: hsl(120, 100%, 40%);">+ * server. For metrics that are generated often or whose value needs to be</span><br><span style="color: hsl(120, 100%, 40%);">+ * stored, metrics can be created and registered. For metrics that can be</span><br><span style="color: hsl(120, 100%, 40%);">+ * obtained "on-the-fly", this mechanism is preferred. When the HTTP route is</span><br><span style="color: hsl(120, 100%, 40%);">+ * queried by promtheus, the registered callbacks are invoked. The string passed</span><br><span style="color: hsl(120, 100%, 40%);">+ * to the callback should be populated with stack-allocated metrics using</span><br><span style="color: hsl(120, 100%, 40%);">+ * \c prometheus_metric_to_string.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Example Usage:</span><br><span style="color: hsl(120, 100%, 40%);">+ * \code</span><br><span style="color: hsl(120, 100%, 40%);">+ * static void prometheus_metric_callback(struct ast_str **output)</span><br><span style="color: hsl(120, 100%, 40%);">+ * {</span><br><span style="color: hsl(120, 100%, 40%);">+ * struct prometheus_metric test_counter =</span><br><span style="color: hsl(120, 100%, 40%);">+ * PROMETHEUS_METRIC_STATIC_INITIALIZATION(</span><br><span style="color: hsl(120, 100%, 40%);">+ * PROMETHEUS_METRIC_COUNTER,</span><br><span style="color: hsl(120, 100%, 40%);">+ * "test_counter",</span><br><span style="color: hsl(120, 100%, 40%);">+ * "A test counter",</span><br><span style="color: hsl(120, 100%, 40%);">+ * NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * prometheus_metric_to_string(&test_counter, output);</span><br><span style="color: hsl(120, 100%, 40%);">+ * }</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * static void load_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+ * {</span><br><span style="color: hsl(120, 100%, 40%);">+ * struct prometheus_callback callback = {</span><br><span style="color: hsl(120, 100%, 40%);">+ * .name = "test_callback",</span><br><span style="color: hsl(120, 100%, 40%);">+ * .callback_fn = &prometheus_metric_callback,</span><br><span style="color: hsl(120, 100%, 40%);">+ * };</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * prometheus_callback_register(&callback);</span><br><span style="color: hsl(120, 100%, 40%);">+ * }</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \endcode</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct prometheus_callback {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief The name of our callback (always useful for debugging)</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *name;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief The callback function to invoke</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ void (* callback_fn)(struct ast_str **output);</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * Register a metric for collection</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param metric The metric to register</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval -1 error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int prometheus_metric_register(struct prometheus_metric *metric);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Remove a registered metric</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param metric The metric to unregister</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note Unregistering also destroys the metric, if found</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 The metric was found, unregistered, and disposed of</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval -1 The metric was not found</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int prometheus_metric_unregister(struct prometheus_metric *metric);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * The current number of registered metrics</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval The current number of registered metrics</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int prometheus_metric_registered_count(void);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * Register a metric callback</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param callback The callback to register</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval -1 error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int prometheus_callback_register(struct prometheus_callback *callback);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Remove a registered callback</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param callback The callback to unregister</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void prometheus_callback_unregister(struct prometheus_callback *callback);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Retrieve the current configuration of the module</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note</span><br><span style="color: hsl(120, 100%, 40%);">+ * This should primarily be done for testing purposes.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \details</span><br><span style="color: hsl(120, 100%, 40%);">+ * config is an AO2 ref counted object</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval NULL on error</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval config on success</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct prometheus_general_config *prometheus_general_config_get(void);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Set the configuration for the module</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note</span><br><span style="color: hsl(120, 100%, 40%);">+ * This should primarily be done for testing purposes</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \details</span><br><span style="color: hsl(120, 100%, 40%);">+ * This is not a ref-stealing function. The reference count to \c config</span><br><span style="color: hsl(120, 100%, 40%);">+ * will be incremented as a result of calling this method.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void prometheus_general_config_set(struct prometheus_general_config *config);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Allocate a new configuration object</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \details</span><br><span style="color: hsl(120, 100%, 40%);">+ * The returned object is an AO2 ref counted object</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval NULL on error</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval config on success</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void *prometheus_general_config_alloc(void);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#endif /* #ifndef RES_PROMETHEUS_H__ */</span><br><span>diff --git a/res/res_prometheus.c b/res/res_prometheus.c</span><br><span>new file mode 100644</span><br><span>index 0000000..1f4e635</span><br><span>--- /dev/null</span><br><span>+++ b/res/res_prometheus.c</span><br><span>@@ -0,0 +1,899 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2019 Sangoma, Inc.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Matt Jordan <mjordan@digium.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \file</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Core Prometheus metrics API</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \author Matt Jordan <mjordan@digium.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*** MODULEINFO</span><br><span style="color: hsl(120, 100%, 40%);">+ <support_level>extended</support_level></span><br><span style="color: hsl(120, 100%, 40%);">+ ***/</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*** DOCUMENTATION</span><br><span style="color: hsl(120, 100%, 40%);">+ <configInfo name="res_prometheus" language="en_US"></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis>Resource for integration with Prometheus</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ <configFile name="prometheus.conf"></span><br><span style="color: hsl(120, 100%, 40%);">+ <configObject name="general"></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis>General settings.</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ <description></span><br><span style="color: hsl(120, 100%, 40%);">+ <para></span><br><span style="color: hsl(120, 100%, 40%);">+ The <emphasis>general</emphasis> settings section contains information</span><br><span style="color: hsl(120, 100%, 40%);">+ to configure Asterisk to serve up statistics for a Prometheus server.</span><br><span style="color: hsl(120, 100%, 40%);">+ </para></span><br><span style="color: hsl(120, 100%, 40%);">+ <note></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>You must enable Asterisk's HTTP server in <filename>http.conf</filename></span><br><span style="color: hsl(120, 100%, 40%);">+ for this module to function properly!</span><br><span style="color: hsl(120, 100%, 40%);">+ </para></span><br><span style="color: hsl(120, 100%, 40%);">+ </note></span><br><span style="color: hsl(120, 100%, 40%);">+ </description></span><br><span style="color: hsl(120, 100%, 40%);">+ <configOption name="enabled" default="no"></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis>Enable or disable Prometheus statistics.</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ <description></span><br><span style="color: hsl(120, 100%, 40%);">+ <enumlist></span><br><span style="color: hsl(120, 100%, 40%);">+ <enum name="no" /></span><br><span style="color: hsl(120, 100%, 40%);">+ <enum name="yes" /></span><br><span style="color: hsl(120, 100%, 40%);">+ </enumlist></span><br><span style="color: hsl(120, 100%, 40%);">+ </description></span><br><span style="color: hsl(120, 100%, 40%);">+ </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+ <configOption name="core_metrics_enabled" default="yes"></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis>Enable or disable core metrics.</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ <description></span><br><span style="color: hsl(120, 100%, 40%);">+ <para></span><br><span style="color: hsl(120, 100%, 40%);">+ Core metrics show various properties of the Asterisk system, including</span><br><span style="color: hsl(120, 100%, 40%);">+ how the binary was built, the version, uptime, last reload time, etc.</span><br><span style="color: hsl(120, 100%, 40%);">+ Generally, these options are harmless and should always be enabled.</span><br><span style="color: hsl(120, 100%, 40%);">+ This option mostly exists to disable output of all options for testing</span><br><span style="color: hsl(120, 100%, 40%);">+ purposes, as well as for those foolish souls who really don't care</span><br><span style="color: hsl(120, 100%, 40%);">+ what version of Asterisk they're running.</span><br><span style="color: hsl(120, 100%, 40%);">+ </para></span><br><span style="color: hsl(120, 100%, 40%);">+ <enumlist></span><br><span style="color: hsl(120, 100%, 40%);">+ <enum name="no" /></span><br><span style="color: hsl(120, 100%, 40%);">+ <enum name="yes" /></span><br><span style="color: hsl(120, 100%, 40%);">+ </enumlist></span><br><span style="color: hsl(120, 100%, 40%);">+ </description></span><br><span style="color: hsl(120, 100%, 40%);">+ </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+ <configOption name="uri" default="metrics"></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis>The HTTP URI to serve metrics up on.</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+ <configOption name="auth_username"></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis>Username to use for Basic Auth.</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ <description></span><br><span style="color: hsl(120, 100%, 40%);">+ <para></span><br><span style="color: hsl(120, 100%, 40%);">+ If set, use Basic Auth to authenticate requests to the route</span><br><span style="color: hsl(120, 100%, 40%);">+ specified by <replaceable>uri</replaceable>. Note that you</span><br><span style="color: hsl(120, 100%, 40%);">+ will need to configure your Prometheus server with the</span><br><span style="color: hsl(120, 100%, 40%);">+ appropriate auth credentials.</span><br><span style="color: hsl(120, 100%, 40%);">+ </para></span><br><span style="color: hsl(120, 100%, 40%);">+ <para></span><br><span style="color: hsl(120, 100%, 40%);">+ If set, <replaceable>auth_password</replaceable> must also</span><br><span style="color: hsl(120, 100%, 40%);">+ be set appropriately.</span><br><span style="color: hsl(120, 100%, 40%);">+ </para></span><br><span style="color: hsl(120, 100%, 40%);">+ <warning></span><br><span style="color: hsl(120, 100%, 40%);">+ <para></span><br><span style="color: hsl(120, 100%, 40%);">+ It is highly recommended to set up Basic Auth. Failure</span><br><span style="color: hsl(120, 100%, 40%);">+ to do so may result in useful information about your</span><br><span style="color: hsl(120, 100%, 40%);">+ Asterisk system being made easily scrapable by the</span><br><span style="color: hsl(120, 100%, 40%);">+ wide world. Consider yourself duly warned.</span><br><span style="color: hsl(120, 100%, 40%);">+ </para></span><br><span style="color: hsl(120, 100%, 40%);">+ </warning></span><br><span style="color: hsl(120, 100%, 40%);">+ </description></span><br><span style="color: hsl(120, 100%, 40%);">+ </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+ <configOption name="auth_password"></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis>Password to use for Basic Auth.</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ <description></span><br><span style="color: hsl(120, 100%, 40%);">+ <para></span><br><span style="color: hsl(120, 100%, 40%);">+ If set, this is used in conjunction with <replaceable>auth_username</replaceable></span><br><span style="color: hsl(120, 100%, 40%);">+ to require Basic Auth for all requests to the Prometheus metrics. Note that</span><br><span style="color: hsl(120, 100%, 40%);">+ setting this without <replaceable>auth_username</replaceable> will not</span><br><span style="color: hsl(120, 100%, 40%);">+ do anything.</span><br><span style="color: hsl(120, 100%, 40%);">+ </para></span><br><span style="color: hsl(120, 100%, 40%);">+ </description></span><br><span style="color: hsl(120, 100%, 40%);">+ </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+ <configOption name="auth_realm" default="Asterisk Prometheus Metrics"></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis>Auth realm used in challenge responses</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+ </configObject></span><br><span style="color: hsl(120, 100%, 40%);">+ </configFile></span><br><span style="color: hsl(120, 100%, 40%);">+ </configInfo></span><br><span style="color: hsl(120, 100%, 40%);">+***/</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define AST_MODULE_SELF_SYM __internal_res_prometheus_self</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/module.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/vector.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/http.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/config_options.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/ast_version.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/buildinfo.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_prometheus.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Lock that protects data structures during an HTTP scrape */</span><br><span style="color: hsl(120, 100%, 40%);">+AST_MUTEX_DEFINE_STATIC(scrape_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_VECTOR(, struct prometheus_metric *) metrics;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_VECTOR(, struct prometheus_callback *) callbacks;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief The actual module config */</span><br><span style="color: hsl(120, 100%, 40%);">+struct module_config {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \brief General settings */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_general_config *general;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct aco_type global_option = {</span><br><span style="color: hsl(120, 100%, 40%);">+ .type = ACO_GLOBAL,</span><br><span style="color: hsl(120, 100%, 40%);">+ .name = "general",</span><br><span style="color: hsl(120, 100%, 40%);">+ .item_offset = offsetof(struct module_config, general),</span><br><span style="color: hsl(120, 100%, 40%);">+ .category_match = ACO_WHITELIST_EXACT,</span><br><span style="color: hsl(120, 100%, 40%);">+ .category = "general",</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct aco_type *global_options[] = ACO_TYPES(&global_option);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct aco_file prometheus_conf = {</span><br><span style="color: hsl(120, 100%, 40%);">+ .filename = "prometheus.conf",</span><br><span style="color: hsl(120, 100%, 40%);">+ .types = ACO_TYPES(&global_option),</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief The module configuration container */</span><br><span style="color: hsl(120, 100%, 40%);">+static AO2_GLOBAL_OBJ_STATIC(global_config);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void *module_config_alloc(void);</span><br><span style="color: hsl(120, 100%, 40%);">+static int prometheus_config_pre_apply(void);</span><br><span style="color: hsl(120, 100%, 40%);">+static void prometheus_config_post_apply(void);</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Register information about the configs being processed by this module */</span><br><span style="color: hsl(120, 100%, 40%);">+CONFIG_INFO_STANDARD(cfg_info, global_config, module_config_alloc,</span><br><span style="color: hsl(120, 100%, 40%);">+ .files = ACO_FILES(&prometheus_conf),</span><br><span style="color: hsl(120, 100%, 40%);">+ .pre_apply_config = prometheus_config_pre_apply,</span><br><span style="color: hsl(120, 100%, 40%);">+ .post_apply_config = prometheus_config_post_apply,</span><br><span style="color: hsl(120, 100%, 40%);">+);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define CORE_PROPERTIES_HELP "Asterisk instance properties. The value of this will always be 1."</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define CORE_UPTIME_HELP "Asterisk instance uptime in seconds."</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define CORE_LAST_RELOAD_HELP "Time since last Asterisk reload in seconds."</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define CORE_METRICS_SCRAPE_TIME_HELP "Total time taken to collect metrics, in milliseconds"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void get_core_uptime_cb(struct prometheus_metric *metric)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct timeval now = ast_tvnow();</span><br><span style="color: hsl(120, 100%, 40%);">+ int64_t duration = ast_tvdiff_sec(now, ast_startuptime);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(metric->value, sizeof(metric->value), "%" PRIu64, duration);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void get_last_reload_cb(struct prometheus_metric *metric)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct timeval now = ast_tvnow();</span><br><span style="color: hsl(120, 100%, 40%);">+ int64_t duration = ast_tvdiff_sec(now, ast_lastreloadtime);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(metric->value, sizeof(metric->value), "%" PRIu64, duration);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief The scrape duration metric</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \details</span><br><span style="color: hsl(120, 100%, 40%);">+ * This metric is special in that it should never be registered.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Instead, the HTTP callback function that walks the metrics will</span><br><span style="color: hsl(120, 100%, 40%);">+ * always populate this metric explicitly if core metrics</span><br><span style="color: hsl(120, 100%, 40%);">+ * are enabled.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct prometheus_metric core_scrape_metric =</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_STATIC_INITIALIZATION(</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_COUNTER,</span><br><span style="color: hsl(120, 100%, 40%);">+ "asterisk_core_scrape_time_ms",</span><br><span style="color: hsl(120, 100%, 40%);">+ CORE_METRICS_SCRAPE_TIME_HELP,</span><br><span style="color: hsl(120, 100%, 40%);">+ NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define METRIC_CORE_PROPS_ARRAY_INDEX 0</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Core metrics to scrape</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct prometheus_metric core_metrics[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_STATIC_INITIALIZATION(</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_COUNTER,</span><br><span style="color: hsl(120, 100%, 40%);">+ "asterisk_core_properties",</span><br><span style="color: hsl(120, 100%, 40%);">+ CORE_PROPERTIES_HELP,</span><br><span style="color: hsl(120, 100%, 40%);">+ NULL),</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_STATIC_INITIALIZATION(</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_COUNTER,</span><br><span style="color: hsl(120, 100%, 40%);">+ "asterisk_core_uptime_seconds",</span><br><span style="color: hsl(120, 100%, 40%);">+ CORE_UPTIME_HELP,</span><br><span style="color: hsl(120, 100%, 40%);">+ get_core_uptime_cb),</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_STATIC_INITIALIZATION(</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_COUNTER,</span><br><span style="color: hsl(120, 100%, 40%);">+ "asterisk_core_last_reload_seconds",</span><br><span style="color: hsl(120, 100%, 40%);">+ CORE_LAST_RELOAD_HELP,</span><br><span style="color: hsl(120, 100%, 40%);">+ get_last_reload_cb),</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/**</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Compare two metrics to see if their name / labels / values match</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param left The first metric to compare</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param right The second metric to compare</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 The metrics are not the same</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 1 The metrics are the same</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int prometheus_metric_cmp(struct prometheus_metric *left,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_metric *right)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int i;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(5, "Comparison: Names %s == %s\n", left->name, right->name);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(left->name, right->name)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < PROMETHEUS_MAX_LABELS; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(5, "Comparison: Label %d Names %s == %s\n", i,</span><br><span style="color: hsl(120, 100%, 40%);">+ left->labels[i].name, right->labels[i].name);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(left->labels[i].name, right->labels[i].name)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(5, "Comparison: Label %d Values %s == %s\n", i,</span><br><span style="color: hsl(120, 100%, 40%);">+ left->labels[i].value, right->labels[i].value);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(left->labels[i].value, right->labels[i].value)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(5, "Copmarison: %s (%p) is equal to %s (%p)\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ left->name, left, right->name, right);</span><br><span style="color: hsl(120, 100%, 40%);">+ return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int prometheus_metric_registered_count(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPED_MUTEX(lock, &scrape_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_VECTOR_SIZE(&metrics);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int prometheus_metric_register(struct prometheus_metric *metric)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPED_MUTEX(lock, &scrape_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+ int i;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!metric) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < AST_VECTOR_SIZE(&metrics); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_metric *existing = AST_VECTOR_GET(&metrics, i);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_metric *child;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (prometheus_metric_cmp(existing, metric)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(AST_LOG_NOTICE,</span><br><span style="color: hsl(120, 100%, 40%);">+ "Refusing registration of existing Prometheus metric: %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ metric->name);</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_LIST_TRAVERSE(&existing->children, child, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (prometheus_metric_cmp(child, metric)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(AST_LOG_NOTICE,</span><br><span style="color: hsl(120, 100%, 40%);">+ "Refusing registration of existing Prometheus metric: %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ metric->name);</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!strcmp(metric->name, existing->name)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(3, "Nesting metric '%s' as child (%p) under existing (%p)\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ metric->name, metric, existing);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_LIST_INSERT_TAIL(&existing->children, metric, entry);</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(3, "Tracking new root metric '%s'\n", metric->name);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (AST_VECTOR_APPEND(&metrics, metric)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(AST_LOG_WARNING, "Failed to grow vector to make room for Prometheus metric: %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ metric->name);</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int prometheus_metric_unregister(struct prometheus_metric *metric)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!metric) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ {</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPED_MUTEX(lock, &scrape_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+ int i;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(3, "Removing metric '%s'\n", metric->name);</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < AST_VECTOR_SIZE(&metrics); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_metric *existing = AST_VECTOR_GET(&metrics, i);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * If this is a complete match, remove the matching metric</span><br><span style="color: hsl(120, 100%, 40%);">+ * and place its children back into the list</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (prometheus_metric_cmp(existing, metric)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_metric *root;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_REMOVE(&metrics, i, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ root = AST_LIST_REMOVE_HEAD(&existing->children, entry);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (root) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_metric *child;</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_LIST_TRAVERSE_SAFE_BEGIN(&existing->children, child, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_LIST_REMOVE_CURRENT(entry);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_LIST_INSERT_TAIL(&root->children, child, entry);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_LIST_TRAVERSE_SAFE_END;</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_INSERT_AT(&metrics, i, root);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_metric_free(existing);</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Name match, but labels don't match. Find the matching entry with</span><br><span style="color: hsl(120, 100%, 40%);">+ * labels and remove it along with all of its children</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!strcmp(existing->name, metric->name)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_metric *child;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_LIST_TRAVERSE_SAFE_BEGIN(&existing->children, child, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (prometheus_metric_cmp(child, metric)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_LIST_REMOVE_CURRENT(entry);</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_metric_free(child);</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_LIST_TRAVERSE_SAFE_END;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void prometheus_metric_free(struct prometheus_metric *metric)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_metric *child;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!metric) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ while ((child = AST_LIST_REMOVE_HEAD(&metric->children, entry))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_metric_free(child);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_mutex_destroy(&metric->lock);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (metric->allocation_strategy == PROMETHEUS_METRIC_ALLOCD) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (metric->allocation_strategy == PROMETHEUS_METRIC_MALLOCD) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(metric);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/**</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Common code for creating a metric</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param name The name of the metric</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param help Help string to output when rendered. This must be static.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval \c prometheus_metric on success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval NULL on failure</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct prometheus_metric *prometheus_metric_create(const char *name, const char *help)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_metric *metric = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ metric = ast_calloc(1, sizeof(*metric));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!metric) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ metric->allocation_strategy = PROMETHEUS_METRIC_MALLOCD;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_mutex_init(&metric->lock);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_copy_string(metric->name, name, sizeof(metric->name));</span><br><span style="color: hsl(120, 100%, 40%);">+ metric->help = help;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return metric;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct prometheus_metric *prometheus_gauge_create(const char *name, const char *help)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_metric *metric;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ metric = prometheus_metric_create(name, help);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!metric) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ metric->type = PROMETHEUS_METRIC_GAUGE;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return metric;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct prometheus_metric *prometheus_counter_create(const char *name, const char *help)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_metric *metric;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ metric = prometheus_metric_create(name, help);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!metric) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ metric->type = PROMETHEUS_METRIC_COUNTER;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return metric;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const char *prometheus_metric_type_to_string(enum prometheus_metric_type type)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (type) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case PROMETHEUS_METRIC_COUNTER:</span><br><span style="color: hsl(120, 100%, 40%);">+ return "counter";</span><br><span style="color: hsl(120, 100%, 40%);">+ case PROMETHEUS_METRIC_GAUGE:</span><br><span style="color: hsl(120, 100%, 40%);">+ return "gauge";</span><br><span style="color: hsl(120, 100%, 40%);">+ default:</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_assert(0);</span><br><span style="color: hsl(120, 100%, 40%);">+ return "unknown";</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/**</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Render a metric to text</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param metric The metric to render</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param output The string buffer to append the text to</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static void prometheus_metric_full_to_string(struct prometheus_metric *metric,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str **output)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int i;</span><br><span style="color: hsl(120, 100%, 40%);">+ int labels_exist = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(output, 0, "%s", metric->name);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < PROMETHEUS_MAX_LABELS; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(metric->labels[i].name)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ labels_exist = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (i == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(output, 0, "%s", "{");</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(output, 0, "%s", ",");</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(output, 0, "%s=\"%s\"",</span><br><span style="color: hsl(120, 100%, 40%);">+ metric->labels[i].name,</span><br><span style="color: hsl(120, 100%, 40%);">+ metric->labels[i].value);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (labels_exist) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(output, 0, "%s", "}");</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * If no value exists, put in a 0. That ensures we don't anger Prometheus.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(metric->value)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(output, 0, " 0\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(output, 0, " %s\n", metric->value);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void prometheus_metric_to_string(struct prometheus_metric *metric,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str **output)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_metric *child;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(output, 0, "# HELP %s %s\n", metric->name, metric->help);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(output, 0, "# TYPE %s %s\n", metric->name,</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_metric_type_to_string(metric->type));</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_metric_full_to_string(metric, output);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_LIST_TRAVERSE(&metric->children, child, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_metric_full_to_string(child, output);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int prometheus_callback_register(struct prometheus_callback *callback)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPED_MUTEX(lock, &scrape_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!callback || !callback->callback_fn || ast_strlen_zero(callback->name)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_APPEND(&callbacks, callback);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void prometheus_callback_unregister(struct prometheus_callback *callback)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPED_MUTEX(lock, &scrape_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+ int i;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < AST_VECTOR_SIZE(&callbacks); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_callback *entry = AST_VECTOR_GET(&callbacks, i);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!strcmp(callback->name, entry->name)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_REMOVE(&callbacks, i, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ return;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int http_callback(struct ast_tcptls_session_instance *ser,</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct ast_http_uri *urih, const char *uri, enum ast_http_method method,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_variable *get_params, struct ast_variable *headers)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(global_config), ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str *response = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct timeval start;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct timeval end;</span><br><span style="color: hsl(120, 100%, 40%);">+ int i;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* If there is no module config or we're not enabled, we can't handle requests */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!mod_cfg || !mod_cfg->general->enabled) {</span><br><span style="color: hsl(120, 100%, 40%);">+ goto err503;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(mod_cfg->general->auth_username)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_http_auth *http_auth;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ http_auth = ast_http_get_auth(headers);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!http_auth) {</span><br><span style="color: hsl(120, 100%, 40%);">+ goto err401;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(http_auth->userid, mod_cfg->general->auth_username)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(5, "Invalid username provided for auth request: %s\n", http_auth->userid);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(http_auth, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ goto err401;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(http_auth->password, mod_cfg->general->auth_password)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(5, "Invalid password provided for auth request: %s\n", http_auth->password);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(http_auth, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ goto err401;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(http_auth, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ response = ast_str_create(512);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!response) {</span><br><span style="color: hsl(120, 100%, 40%);">+ goto err500;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (mod_cfg->general->core_metrics_enabled) {</span><br><span style="color: hsl(120, 100%, 40%);">+ start = ast_tvnow();</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_mutex_lock(&scrape_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < AST_VECTOR_SIZE(&callbacks); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_callback *callback = AST_VECTOR_GET(&callbacks, i);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ callback->callback_fn(&response);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < AST_VECTOR_SIZE(&metrics); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_metric *metric = AST_VECTOR_GET(&metrics, i);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_mutex_lock(&metric->lock);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (metric->get_metric_value) {</span><br><span style="color: hsl(120, 100%, 40%);">+ metric->get_metric_value(metric);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_metric_to_string(metric, &response);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_mutex_unlock(&metric->lock);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (mod_cfg->general->core_metrics_enabled) {</span><br><span style="color: hsl(120, 100%, 40%);">+ int64_t duration;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ end = ast_tvnow();</span><br><span style="color: hsl(120, 100%, 40%);">+ duration = ast_tvdiff_ms(end, start);</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(core_scrape_metric.value,</span><br><span style="color: hsl(120, 100%, 40%);">+ sizeof(core_scrape_metric.value),</span><br><span style="color: hsl(120, 100%, 40%);">+ "%" PRIu64,</span><br><span style="color: hsl(120, 100%, 40%);">+ duration);</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_metric_to_string(&core_scrape_metric, &response);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_mutex_unlock(&scrape_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_http_send(ser, method, 200, "OK", NULL, response, 0, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+err401:</span><br><span style="color: hsl(120, 100%, 40%);">+ {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str *auth_challenge_headers;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ auth_challenge_headers = ast_str_create(128);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!auth_challenge_headers) {</span><br><span style="color: hsl(120, 100%, 40%);">+ goto err500;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&auth_challenge_headers, 0,</span><br><span style="color: hsl(120, 100%, 40%);">+ "WWW-Authenticate: Basic realm=\"%s\"\r\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ mod_cfg->general->auth_realm);</span><br><span style="color: hsl(120, 100%, 40%);">+ /* ast_http_send takes ownership of the ast_str */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_http_send(ser, method, 401, "Unauthorized", auth_challenge_headers, NULL, 0, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(response);</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+err503:</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_http_send(ser, method, 503, "Service Unavailable", NULL, NULL, 0, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(response);</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+err500:</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_http_send(ser, method, 500, "Server Error", NULL, NULL, 0, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(response);</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void prometheus_general_config_dtor(void *obj)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_general_config *config = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_string_field_free_memory(config);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void *prometheus_general_config_alloc(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_general_config *config;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ config = ao2_alloc(sizeof(*config), prometheus_general_config_dtor);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!config || ast_string_field_init(config, 32)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return config;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct prometheus_general_config *prometheus_general_config_get(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(global_config), ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!mod_cfg) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_bump(mod_cfg->general);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return mod_cfg->general;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void prometheus_general_config_set(struct prometheus_general_config *config)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(global_config), ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!mod_cfg) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_replace(mod_cfg->general, config);</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_config_post_apply();</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Configuration object destructor */</span><br><span style="color: hsl(120, 100%, 40%);">+static void module_config_dtor(void *obj)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct module_config *config = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (config->general) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(config->general, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Module config constructor */</span><br><span style="color: hsl(120, 100%, 40%);">+static void *module_config_alloc(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct module_config *config;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ config = ao2_alloc(sizeof(*config), module_config_dtor);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!config) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ config->general = prometheus_general_config_alloc();</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!config->general) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(config, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ config = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return config;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_http_uri prometheus_uri = {</span><br><span style="color: hsl(120, 100%, 40%);">+ .description = "Prometheus Metrics URI",</span><br><span style="color: hsl(120, 100%, 40%);">+ .callback = http_callback,</span><br><span style="color: hsl(120, 100%, 40%);">+ .has_subtree = 1,</span><br><span style="color: hsl(120, 100%, 40%);">+ .data = NULL,</span><br><span style="color: hsl(120, 100%, 40%);">+ .key = __FILE__,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Pre-apply callback for the config framework.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This validates that required fields exist and are populated.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int prometheus_config_pre_apply(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct module_config *config = aco_pending_config(&cfg_info);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!config->general->enabled) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* If we're not enabled, we don't care about anything else */</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(config->general->auth_username)</span><br><span style="color: hsl(120, 100%, 40%);">+ && ast_strlen_zero(config->general->auth_password)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(AST_LOG_ERROR, "'auth_username' set without a corresponding 'auth_password'\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Post-apply callback for the config framework.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This sets any run-time information derived from the configuration</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static void prometheus_config_post_apply(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(global_config), ao2_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+ int i;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* We can get away with this as the lifetime of the URI</span><br><span style="color: hsl(120, 100%, 40%);">+ * registered with the HTTP core is contained within</span><br><span style="color: hsl(120, 100%, 40%);">+ * the lifetime of the module configuration</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_uri.uri = mod_cfg->general->uri;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Re-register the core metrics */</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < ARRAY_LEN(core_metrics); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_metric_unregister(&core_metrics[i]);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (mod_cfg->general->core_metrics_enabled) {</span><br><span style="color: hsl(120, 100%, 40%);">+ char eid_str[32];</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_SET_LABEL(&core_scrape_metric, 0, "eid", eid_str);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_SET_LABEL(&core_metrics[METRIC_CORE_PROPS_ARRAY_INDEX],</span><br><span style="color: hsl(120, 100%, 40%);">+ 1, "version", ast_get_version());</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_SET_LABEL(&core_metrics[METRIC_CORE_PROPS_ARRAY_INDEX],</span><br><span style="color: hsl(120, 100%, 40%);">+ 2, "build_options", ast_get_build_opts());</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_SET_LABEL(&core_metrics[METRIC_CORE_PROPS_ARRAY_INDEX],</span><br><span style="color: hsl(120, 100%, 40%);">+ 3, "build_date", ast_build_date);</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_SET_LABEL(&core_metrics[METRIC_CORE_PROPS_ARRAY_INDEX],</span><br><span style="color: hsl(120, 100%, 40%);">+ 4, "build_os", ast_build_os);</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_SET_LABEL(&core_metrics[METRIC_CORE_PROPS_ARRAY_INDEX],</span><br><span style="color: hsl(120, 100%, 40%);">+ 5, "build_kernel", ast_build_kernel);</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_SET_LABEL(&core_metrics[METRIC_CORE_PROPS_ARRAY_INDEX],</span><br><span style="color: hsl(120, 100%, 40%);">+ 6, "build_host", ast_build_hostname);</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(core_metrics[METRIC_CORE_PROPS_ARRAY_INDEX].value,</span><br><span style="color: hsl(120, 100%, 40%);">+ sizeof(core_metrics[METRIC_CORE_PROPS_ARRAY_INDEX].value),</span><br><span style="color: hsl(120, 100%, 40%);">+ "%d", 1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < ARRAY_LEN(core_metrics); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_SET_LABEL(&core_metrics[i], 0, "eid", eid_str);</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_metric_register(&core_metrics[i]);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int unload_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPED_MUTEX(lock, &scrape_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+ int i;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_http_uri_unlink(&prometheus_uri);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < AST_VECTOR_SIZE(&metrics); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_metric *metric = AST_VECTOR_GET(&metrics, i);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_metric_free(metric);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_FREE(&metrics);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_FREE(&callbacks);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ aco_info_destroy(&cfg_info);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_global_obj_release(global_config);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int reload_module(void) {</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPED_MUTEX(lock, &scrape_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_http_uri_unlink(&prometheus_uri);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_http_uri_link(&prometheus_uri)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(AST_LOG_WARNING, "Failed to re-register Prometheus Metrics URI during reload\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int load_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPED_MUTEX(lock, &scrape_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (AST_VECTOR_INIT(&metrics, 64)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (AST_VECTOR_INIT(&callbacks, 8)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (aco_info_init(&cfg_info)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ aco_option_register(&cfg_info, "enabled", ACO_EXACT, global_options, "no", OPT_BOOL_T, 1, FLDSET(struct prometheus_general_config, enabled));</span><br><span style="color: hsl(120, 100%, 40%);">+ aco_option_register(&cfg_info, "core_metrics_enabled", ACO_EXACT, global_options, "yes", OPT_BOOL_T, 1, FLDSET(struct prometheus_general_config, core_metrics_enabled));</span><br><span style="color: hsl(120, 100%, 40%);">+ aco_option_register(&cfg_info, "uri", ACO_EXACT, global_options, "", OPT_STRINGFIELD_T, 1, STRFLDSET(struct prometheus_general_config, uri));</span><br><span style="color: hsl(120, 100%, 40%);">+ aco_option_register(&cfg_info, "auth_username", ACO_EXACT, global_options, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct prometheus_general_config, auth_username));</span><br><span style="color: hsl(120, 100%, 40%);">+ aco_option_register(&cfg_info, "auth_password", ACO_EXACT, global_options, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct prometheus_general_config, auth_password));</span><br><span style="color: hsl(120, 100%, 40%);">+ aco_option_register(&cfg_info, "auth_realm", ACO_EXACT, global_options, "Asterisk Prometheus Metrics", OPT_STRINGFIELD_T, 0, STRFLDSET(struct prometheus_general_config, auth_realm));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {</span><br><span style="color: hsl(120, 100%, 40%);">+ goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_http_uri_link(&prometheus_uri)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ goto cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_MODULE_LOAD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+cleanup:</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_http_uri_unlink(&prometheus_uri);</span><br><span style="color: hsl(120, 100%, 40%);">+ aco_info_destroy(&cfg_info);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_FREE(&metrics);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_FREE(&callbacks);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_MODULE_LOAD_DECLINE;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Asterisk Prometheus Module",</span><br><span style="color: hsl(120, 100%, 40%);">+ .support_level = AST_MODULE_SUPPORT_EXTENDED,</span><br><span style="color: hsl(120, 100%, 40%);">+ .load = load_module,</span><br><span style="color: hsl(120, 100%, 40%);">+ .unload = unload_module,</span><br><span style="color: hsl(120, 100%, 40%);">+ .reload = reload_module,</span><br><span style="color: hsl(120, 100%, 40%);">+ .load_pri = AST_MODPRI_DEFAULT,</span><br><span style="color: hsl(120, 100%, 40%);">+);</span><br><span>diff --git a/res/res_prometheus.exports.in b/res/res_prometheus.exports.in</span><br><span>new file mode 100644</span><br><span>index 0000000..cec31c6</span><br><span>--- /dev/null</span><br><span>+++ b/res/res_prometheus.exports.in</span><br><span>@@ -0,0 +1,6 @@</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ global:</span><br><span style="color: hsl(120, 100%, 40%);">+ LINKER_SYMBOL_PREFIXprometheus*;</span><br><span style="color: hsl(120, 100%, 40%);">+ local:</span><br><span style="color: hsl(120, 100%, 40%);">+ *;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span>diff --git a/tests/test_res_prometheus.c b/tests/test_res_prometheus.c</span><br><span>new file mode 100644</span><br><span>index 0000000..01279be</span><br><span>--- /dev/null</span><br><span>+++ b/tests/test_res_prometheus.c</span><br><span>@@ -0,0 +1,829 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2019 Sangoma, Inc.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Matt Jordan <mjordan@digium.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*** MODULEINFO</span><br><span style="color: hsl(120, 100%, 40%);">+ <depend>TEST_FRAMEWORK</depend></span><br><span style="color: hsl(120, 100%, 40%);">+ <depend>res_prometheus</depend></span><br><span style="color: hsl(120, 100%, 40%);">+ <depend>curl</depend></span><br><span style="color: hsl(120, 100%, 40%);">+ <support_level>extended</support_level></span><br><span style="color: hsl(120, 100%, 40%);">+ ***/</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <curl/curl.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/test.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/module.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/config.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_prometheus.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define CATEGORY "/res/prometheus/"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static char server_uri[512];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct prometheus_general_config *module_config;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void curl_free_wrapper(void *ptr)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ptr) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ curl_easy_cleanup(ptr);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void prometheus_metric_free_wrapper(void *ptr)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (prometheus_metric_unregister(ptr)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_metric_free(ptr);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define GLOBAL_USERAGENT "asterisk-libcurl-agent/1.0"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct prometheus_general_config *config_alloc(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_general_config *config;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ config = prometheus_general_config_alloc();</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!config) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Set what we need on the config for most tests */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_string_field_set(config, uri, "test_metrics");</span><br><span style="color: hsl(120, 100%, 40%);">+ config->enabled = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ config->core_metrics_enabled = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return config;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static CURL *get_curl_instance(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ CURL *curl;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ curl = curl_easy_init();</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!curl) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ curl_easy_setopt(curl, CURLOPT_TIMEOUT, 180);</span><br><span style="color: hsl(120, 100%, 40%);">+ curl_easy_setopt(curl, CURLOPT_USERAGENT, GLOBAL_USERAGENT);</span><br><span style="color: hsl(120, 100%, 40%);">+ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ curl_easy_setopt(curl, CURLOPT_URL, server_uri);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return curl;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static size_t curl_write_string_callback(void *contents, size_t size, size_t nmemb, void *userdata)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str **buffer = userdata;</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t realsize = size * nmemb;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *rawdata;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ rawdata = ast_malloc(realsize + 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!rawdata) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ memcpy(rawdata, contents, realsize);</span><br><span style="color: hsl(120, 100%, 40%);">+ rawdata[realsize] = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(buffer, 0, "%s", rawdata);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(rawdata);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return realsize;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void metric_values_get_counter_value_cb(struct prometheus_metric *metric)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ strcpy(metric->value, "2");</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_TEST_DEFINE(metric_values)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(CURL *, curl, NULL, curl_free_wrapper);</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(struct ast_str *, buffer, NULL, ast_free);</span><br><span style="color: hsl(120, 100%, 40%);">+ int res;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_metric test_counter_one = PROMETHEUS_METRIC_STATIC_INITIALIZATION(</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_COUNTER,</span><br><span style="color: hsl(120, 100%, 40%);">+ "test_counter_one",</span><br><span style="color: hsl(120, 100%, 40%);">+ "A test counter",</span><br><span style="color: hsl(120, 100%, 40%);">+ NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_metric test_counter_two = PROMETHEUS_METRIC_STATIC_INITIALIZATION(</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_COUNTER,</span><br><span style="color: hsl(120, 100%, 40%);">+ "test_counter_two",</span><br><span style="color: hsl(120, 100%, 40%);">+ "A test counter",</span><br><span style="color: hsl(120, 100%, 40%);">+ metric_values_get_counter_value_cb);</span><br><span style="color: hsl(120, 100%, 40%);">+ enum ast_test_result_state result = AST_TEST_PASS;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case TEST_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+ info->name = __func__;</span><br><span style="color: hsl(120, 100%, 40%);">+ info->category = CATEGORY;</span><br><span style="color: hsl(120, 100%, 40%);">+ info->summary = "Test value generation/respecting in metrics";</span><br><span style="color: hsl(120, 100%, 40%);">+ info->description =</span><br><span style="color: hsl(120, 100%, 40%);">+ "Metrics have two ways to provide values when the HTTP callback\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "is invoked:\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "1. By using the direct value that resides in the metric\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "2. By providing a callback function to specify the value\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "This test verifies that both function appropriately when the\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "HTTP callback is called.";</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+ case TEST_EXECUTE:</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ buffer = ast_str_create(128);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!buffer) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ curl = get_curl_instance();</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!curl) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate_cleanup(test, prometheus_metric_register(&test_counter_one) == 0, result, metric_values_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate_cleanup(test, prometheus_metric_register(&test_counter_two) == 0, result, metric_values_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+ strcpy(test_counter_one.value, "1");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, " -> CURLing request...\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_string_callback);</span><br><span style="color: hsl(120, 100%, 40%);">+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = curl_easy_perform(curl);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (res != CURLE_OK) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Failed to execute CURL: %d\n", res);</span><br><span style="color: hsl(120, 100%, 40%);">+ result = AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ goto metric_values_cleanup;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, " -> Retrieved: %s\n", ast_str_buffer(buffer));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate_cleanup(test, strcmp(ast_str_buffer(buffer),</span><br><span style="color: hsl(120, 100%, 40%);">+ "# HELP test_counter_one A test counter\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "# TYPE test_counter_one counter\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "test_counter_one 1\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "# HELP test_counter_two A test counter\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "# TYPE test_counter_two counter\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "test_counter_two 2\n") == 0, result, metric_values_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+metric_values_cleanup:</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_metric_unregister(&test_counter_one);</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_metric_unregister(&test_counter_two);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return result;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void prometheus_metric_callback(struct ast_str **output)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_metric test_counter = PROMETHEUS_METRIC_STATIC_INITIALIZATION(</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_COUNTER,</span><br><span style="color: hsl(120, 100%, 40%);">+ "test_counter",</span><br><span style="color: hsl(120, 100%, 40%);">+ "A test counter",</span><br><span style="color: hsl(120, 100%, 40%);">+ NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_metric_to_string(&test_counter, output);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_TEST_DEFINE(metric_callback_register)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(CURL *, curl, NULL, curl_free_wrapper);</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(struct ast_str *, buffer, NULL, ast_free);</span><br><span style="color: hsl(120, 100%, 40%);">+ int res;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_callback callback = {</span><br><span style="color: hsl(120, 100%, 40%);">+ .name = "test_callback",</span><br><span style="color: hsl(120, 100%, 40%);">+ .callback_fn = &prometheus_metric_callback,</span><br><span style="color: hsl(120, 100%, 40%);">+ };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case TEST_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+ info->name = __func__;</span><br><span style="color: hsl(120, 100%, 40%);">+ info->category = CATEGORY;</span><br><span style="color: hsl(120, 100%, 40%);">+ info->summary = "Test registration of callbacks";</span><br><span style="color: hsl(120, 100%, 40%);">+ info->description =</span><br><span style="color: hsl(120, 100%, 40%);">+ "This test covers callback registration. It registers\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "a callback that is invoked when an HTTP request is made,\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "and it verifies that during said callback the output to\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "the response string is correctly appended to. It also verifies\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "that unregistered callbacks are not invoked.";</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+ case TEST_EXECUTE:</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ buffer = ast_str_create(128);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!buffer) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate(test, prometheus_callback_register(&callback) == 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ curl = get_curl_instance();</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!curl) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, " -> CURLing request...\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_string_callback);</span><br><span style="color: hsl(120, 100%, 40%);">+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = curl_easy_perform(curl);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (res != CURLE_OK) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Failed to execute CURL: %d\n", res);</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, " -> Retrieved: %s\n", ast_str_buffer(buffer));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate(test, strcmp(ast_str_buffer(buffer),</span><br><span style="color: hsl(120, 100%, 40%);">+ "# HELP test_counter A test counter\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "# TYPE test_counter counter\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "test_counter 0\n") == 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_callback_unregister(&callback);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_PASS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_TEST_DEFINE(metric_register)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_metric test_counter = PROMETHEUS_METRIC_STATIC_INITIALIZATION(</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_COUNTER,</span><br><span style="color: hsl(120, 100%, 40%);">+ "test_counter",</span><br><span style="color: hsl(120, 100%, 40%);">+ "A test counter",</span><br><span style="color: hsl(120, 100%, 40%);">+ NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(struct prometheus_metric *, test_gauge, NULL, prometheus_metric_free_wrapper);</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(struct prometheus_metric *, test_gauge_child_one, NULL, prometheus_metric_free_wrapper);</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(struct prometheus_metric *, test_gauge_child_two, NULL, prometheus_metric_free_wrapper);</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(struct prometheus_metric *, bad_metric, NULL, prometheus_metric_free_wrapper);</span><br><span style="color: hsl(120, 100%, 40%);">+ enum ast_test_result_state result;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case TEST_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+ info->name = __func__;</span><br><span style="color: hsl(120, 100%, 40%);">+ info->category = CATEGORY;</span><br><span style="color: hsl(120, 100%, 40%);">+ info->summary = "Test registration of metrics";</span><br><span style="color: hsl(120, 100%, 40%);">+ info->description =</span><br><span style="color: hsl(120, 100%, 40%);">+ "This test covers the following registration scenarios:\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "- Nominal registration of simple metrics\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "- Registration of metrics with different allocation strategies\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "- Nested metrics with label families\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "- Off nominal registration with simple name collisions\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "- Off nominal registration with label collisions";</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+ case TEST_EXECUTE:</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Testing nominal registration\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "-> Static metric\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate_cleanup(test, prometheus_metric_register(&test_counter) == 0, result, metric_register_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "-> Malloc'd metric\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ test_gauge = prometheus_gauge_create("test_gauge", "A test gauge");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate(test, test_gauge != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate_cleanup(test, prometheus_metric_register(test_gauge) == 0, result, metric_register_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate_cleanup(test, prometheus_metric_registered_count() == 2, result, metric_register_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Testing nominal registration of child metrics\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ test_gauge_child_one = prometheus_gauge_create("test_gauge", "A test gauge");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate_cleanup(test, test_gauge_child_one != NULL, result, metric_register_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_SET_LABEL(test_gauge_child_one, 0, "key_one", "value_one");</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_SET_LABEL(test_gauge_child_one, 1, "key_two", "value_one");</span><br><span style="color: hsl(120, 100%, 40%);">+ test_gauge_child_two = prometheus_gauge_create("test_gauge", "A test gauge");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate_cleanup(test, test_gauge_child_two != NULL, result, metric_register_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_SET_LABEL(test_gauge_child_two, 0, "key_one", "value_two");</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_SET_LABEL(test_gauge_child_two, 1, "key_two", "value_two");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate_cleanup(test, prometheus_metric_register(test_gauge_child_one) == 0, result, metric_register_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate_cleanup(test, prometheus_metric_register(test_gauge_child_two) == 0, result, metric_register_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate_cleanup(test, prometheus_metric_registered_count() == 2, result, metric_register_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate_cleanup(test, test_gauge->children.first == test_gauge_child_one, result, metric_register_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate_cleanup(test, test_gauge->children.last == test_gauge_child_two, result, metric_register_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Testing name collisions\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ bad_metric = prometheus_counter_create("test_counter", "A test counter");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate_cleanup(test, bad_metric != NULL, result, metric_register_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate_cleanup(test, prometheus_metric_register(bad_metric) != 0, result, metric_register_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_metric_free(bad_metric);</span><br><span style="color: hsl(120, 100%, 40%);">+ bad_metric = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Testing label collisions\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ bad_metric = prometheus_gauge_create("test_gauge", "A test gauge");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate_cleanup(test, bad_metric != NULL, result, metric_register_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_SET_LABEL(bad_metric, 0, "key_one", "value_one");</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_SET_LABEL(bad_metric, 1, "key_two", "value_one");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate_cleanup(test, prometheus_metric_register(bad_metric) != 0, result, metric_register_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_metric_free(bad_metric);</span><br><span style="color: hsl(120, 100%, 40%);">+ bad_metric = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Testing removal of metrics\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_metric_unregister(test_gauge_child_two);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_gauge_child_two = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate_cleanup(test, prometheus_metric_registered_count() == 2, result, metric_register_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_metric_unregister(test_gauge);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_gauge = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate_cleanup(test, prometheus_metric_registered_count() == 2, result, metric_register_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_metric_unregister(test_gauge_child_one);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_gauge_child_one = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate_cleanup(test, prometheus_metric_registered_count() == 1, result, metric_register_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_metric_unregister(&test_counter);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate_cleanup(test, prometheus_metric_registered_count() == 0, result, metric_register_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_PASS;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+metric_register_cleanup:</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_metric_unregister(&test_counter);</span><br><span style="color: hsl(120, 100%, 40%);">+ return result;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_TEST_DEFINE(counter_to_string)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_metric test_counter = PROMETHEUS_METRIC_STATIC_INITIALIZATION(</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_COUNTER,</span><br><span style="color: hsl(120, 100%, 40%);">+ "test_counter",</span><br><span style="color: hsl(120, 100%, 40%);">+ "A test counter",</span><br><span style="color: hsl(120, 100%, 40%);">+ NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_metric test_counter_child_one = PROMETHEUS_METRIC_STATIC_INITIALIZATION(</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_COUNTER,</span><br><span style="color: hsl(120, 100%, 40%);">+ "test_counter",</span><br><span style="color: hsl(120, 100%, 40%);">+ "A test counter",</span><br><span style="color: hsl(120, 100%, 40%);">+ NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_metric test_counter_child_two = PROMETHEUS_METRIC_STATIC_INITIALIZATION(</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_COUNTER,</span><br><span style="color: hsl(120, 100%, 40%);">+ "test_counter",</span><br><span style="color: hsl(120, 100%, 40%);">+ "A test counter",</span><br><span style="color: hsl(120, 100%, 40%);">+ NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(struct ast_str *, buffer, NULL, ast_free);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case TEST_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+ info->name = __func__;</span><br><span style="color: hsl(120, 100%, 40%);">+ info->category = CATEGORY;</span><br><span style="color: hsl(120, 100%, 40%);">+ info->summary = "Test formatting of counters";</span><br><span style="color: hsl(120, 100%, 40%);">+ info->description =</span><br><span style="color: hsl(120, 100%, 40%);">+ "This test covers the formatting of printed counters";</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+ case TEST_EXECUTE:</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ buffer = ast_str_create(128);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!buffer) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_SET_LABEL(&test_counter_child_one, 0, "key_one", "value_one");</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_SET_LABEL(&test_counter_child_one, 1, "key_two", "value_one");</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_SET_LABEL(&test_counter_child_two, 0, "key_one", "value_two");</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_SET_LABEL(&test_counter_child_two, 1, "key_two", "value_two");</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_LIST_INSERT_TAIL(&test_counter.children, &test_counter_child_one, entry);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_LIST_INSERT_TAIL(&test_counter.children, &test_counter_child_two, entry);</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_metric_to_string(&test_counter, &buffer);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate(test, strcmp(ast_str_buffer(buffer),</span><br><span style="color: hsl(120, 100%, 40%);">+ "# HELP test_counter A test counter\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "# TYPE test_counter counter\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "test_counter 0\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "test_counter{key_one=\"value_one\",key_two=\"value_one\"} 0\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "test_counter{key_one=\"value_two\",key_two=\"value_two\"} 0\n") == 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_PASS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_TEST_DEFINE(counter_create)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(struct prometheus_metric *, metric, NULL, prometheus_metric_free_wrapper);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case TEST_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+ info->name = __func__;</span><br><span style="color: hsl(120, 100%, 40%);">+ info->category = CATEGORY;</span><br><span style="color: hsl(120, 100%, 40%);">+ info->summary = "Test creation (and destruction) of malloc'd counters";</span><br><span style="color: hsl(120, 100%, 40%);">+ info->description =</span><br><span style="color: hsl(120, 100%, 40%);">+ "This test covers creating a counter metric and destroying\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "it. The metric should be malloc'd.";</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+ case TEST_EXECUTE:</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ metric = prometheus_counter_create("test_counter", "A test counter");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate(test, metric != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate(test, metric->type == PROMETHEUS_METRIC_COUNTER);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate(test, metric->allocation_strategy = PROMETHEUS_METRIC_MALLOCD);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate(test, !strcmp(metric->help, "A test counter"));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate(test, !strcmp(metric->name, "test_counter"));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate(test, !strcmp(metric->value, ""));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate(test, metric->children.first == NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate(test, metric->children.last == NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_PASS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_TEST_DEFINE(gauge_to_string)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_metric test_gauge = PROMETHEUS_METRIC_STATIC_INITIALIZATION(</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_GAUGE,</span><br><span style="color: hsl(120, 100%, 40%);">+ "test_gauge",</span><br><span style="color: hsl(120, 100%, 40%);">+ "A test gauge",</span><br><span style="color: hsl(120, 100%, 40%);">+ NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_metric test_gauge_child_one = PROMETHEUS_METRIC_STATIC_INITIALIZATION(</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_GAUGE,</span><br><span style="color: hsl(120, 100%, 40%);">+ "test_gauge",</span><br><span style="color: hsl(120, 100%, 40%);">+ "A test gauge",</span><br><span style="color: hsl(120, 100%, 40%);">+ NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_metric test_gauge_child_two = PROMETHEUS_METRIC_STATIC_INITIALIZATION(</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_GAUGE,</span><br><span style="color: hsl(120, 100%, 40%);">+ "test_gauge",</span><br><span style="color: hsl(120, 100%, 40%);">+ "A test gauge",</span><br><span style="color: hsl(120, 100%, 40%);">+ NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(struct ast_str *, buffer, NULL, ast_free);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case TEST_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+ info->name = __func__;</span><br><span style="color: hsl(120, 100%, 40%);">+ info->category = CATEGORY;</span><br><span style="color: hsl(120, 100%, 40%);">+ info->summary = "Test formatting of gauges";</span><br><span style="color: hsl(120, 100%, 40%);">+ info->description =</span><br><span style="color: hsl(120, 100%, 40%);">+ "This test covers the formatting of printed gauges";</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+ case TEST_EXECUTE:</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ buffer = ast_str_create(128);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!buffer) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_SET_LABEL(&test_gauge_child_one, 0, "key_one", "value_one");</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_SET_LABEL(&test_gauge_child_one, 1, "key_two", "value_one");</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_SET_LABEL(&test_gauge_child_two, 0, "key_one", "value_two");</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_SET_LABEL(&test_gauge_child_two, 1, "key_two", "value_two");</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_LIST_INSERT_TAIL(&test_gauge.children, &test_gauge_child_one, entry);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_LIST_INSERT_TAIL(&test_gauge.children, &test_gauge_child_two, entry);</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_metric_to_string(&test_gauge, &buffer);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate(test, strcmp(ast_str_buffer(buffer),</span><br><span style="color: hsl(120, 100%, 40%);">+ "# HELP test_gauge A test gauge\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "# TYPE test_gauge gauge\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "test_gauge 0\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "test_gauge{key_one=\"value_one\",key_two=\"value_one\"} 0\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "test_gauge{key_one=\"value_two\",key_two=\"value_two\"} 0\n") == 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_PASS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_TEST_DEFINE(gauge_create)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(struct prometheus_metric *, metric, NULL, prometheus_metric_free_wrapper);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case TEST_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+ info->name = __func__;</span><br><span style="color: hsl(120, 100%, 40%);">+ info->category = CATEGORY;</span><br><span style="color: hsl(120, 100%, 40%);">+ info->summary = "Test creation (and destruction) of malloc'd gauges";</span><br><span style="color: hsl(120, 100%, 40%);">+ info->description =</span><br><span style="color: hsl(120, 100%, 40%);">+ "This test covers creating a gauge metric and destroying\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "it. The metric should be malloc'd.";</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+ case TEST_EXECUTE:</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ metric = prometheus_gauge_create("test_gauge", "A test gauge");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate(test, metric != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate(test, metric->type == PROMETHEUS_METRIC_GAUGE);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate(test, metric->allocation_strategy = PROMETHEUS_METRIC_MALLOCD);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate(test, !strcmp(metric->help, "A test gauge"));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate(test, !strcmp(metric->name, "test_gauge"));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate(test, !strcmp(metric->value, ""));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate(test, metric->children.first == NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate(test, metric->children.last == NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_PASS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_TEST_DEFINE(config_general_basic_auth)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(CURL *, curl, NULL, curl_free_wrapper);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_general_config *config;</span><br><span style="color: hsl(120, 100%, 40%);">+ int res;</span><br><span style="color: hsl(120, 100%, 40%);">+ long response_code;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case TEST_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+ info->name = __func__;</span><br><span style="color: hsl(120, 100%, 40%);">+ info->category = CATEGORY;</span><br><span style="color: hsl(120, 100%, 40%);">+ info->summary = "Test basic auth handling";</span><br><span style="color: hsl(120, 100%, 40%);">+ info->description =</span><br><span style="color: hsl(120, 100%, 40%);">+ "This test covers authentication of requests";</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+ case TEST_EXECUTE:</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ config = config_alloc();</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!config) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_string_field_set(config, auth_username, "foo");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_string_field_set(config, auth_password, "bar");</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Prometheus module owns the ref after this call */</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_general_config_set(config);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(config, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ curl = get_curl_instance();</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!curl) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Testing without auth credentials\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, " -> CURLing request...\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ res = curl_easy_perform(curl);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (res != CURLE_OK) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Failed to execute CURL: %d\n", res);</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, " -> CURL returned %ld\n", response_code);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate(test, response_code == 401);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Testing with invalid auth credentials\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, " -> CURLing request...\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);</span><br><span style="color: hsl(120, 100%, 40%);">+ curl_easy_setopt(curl, CURLOPT_USERPWD, "matt:jordan");</span><br><span style="color: hsl(120, 100%, 40%);">+ res = curl_easy_perform(curl);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (res != CURLE_OK) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Failed to execute CURL: %d\n", res);</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, " -> CURL returned %ld\n", response_code);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate(test, response_code == 401);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Testing with valid auth credentials\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, " -> CURLing request...\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ curl_easy_setopt(curl, CURLOPT_USERPWD, "foo:bar");</span><br><span style="color: hsl(120, 100%, 40%);">+ res = curl_easy_perform(curl);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (res != CURLE_OK) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Failed to execute CURL: %d\n", res);</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, " -> CURL returned %ld\n", response_code);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate(test, response_code == 200);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_PASS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_TEST_DEFINE(config_general_enabled)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(CURL *, curl, NULL, curl_free_wrapper);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_general_config *config;</span><br><span style="color: hsl(120, 100%, 40%);">+ int res;</span><br><span style="color: hsl(120, 100%, 40%);">+ long response_code;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case TEST_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+ info->name = __func__;</span><br><span style="color: hsl(120, 100%, 40%);">+ info->category = CATEGORY;</span><br><span style="color: hsl(120, 100%, 40%);">+ info->summary = "Test handling of enable/disable";</span><br><span style="color: hsl(120, 100%, 40%);">+ info->description =</span><br><span style="color: hsl(120, 100%, 40%);">+ "When disabled, the module should return a 503.\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "This test verifies that it actually occurs.";</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+ case TEST_EXECUTE:</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ config = config_alloc();</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!config) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ config->enabled = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Prometheus module owns the ref after this call */</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_general_config_set(config);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(config, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ curl = get_curl_instance();</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!curl) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, " -> CURLing request...\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ res = curl_easy_perform(curl);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (res != CURLE_OK) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Failed to execute CURL: %d\n", res);</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, " -> CURL returned %ld\n", response_code);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate(test, response_code == 503);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_PASS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_TEST_DEFINE(config_general_core_metrics)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(CURL *, curl, NULL, curl_free_wrapper);</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(struct ast_str *, buffer, NULL, ast_free);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_general_config *config;</span><br><span style="color: hsl(120, 100%, 40%);">+ int res;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case TEST_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+ info->name = __func__;</span><br><span style="color: hsl(120, 100%, 40%);">+ info->category = CATEGORY;</span><br><span style="color: hsl(120, 100%, 40%);">+ info->summary = "Test producing core metrics";</span><br><span style="color: hsl(120, 100%, 40%);">+ info->description =</span><br><span style="color: hsl(120, 100%, 40%);">+ "This test covers the core metrics that are produced\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "by the basic Prometheus module.";</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+ case TEST_EXECUTE:</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ buffer = ast_str_create(128);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!buffer) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ config = config_alloc();</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!config) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ config->core_metrics_enabled = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Prometheus module owns the ref after this call */</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_general_config_set(config);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(config, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ curl = get_curl_instance();</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!curl) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, " -> CURLing request...\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_string_callback);</span><br><span style="color: hsl(120, 100%, 40%);">+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = curl_easy_perform(curl);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (res != CURLE_OK) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Failed to execute CURL: %d\n", res);</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_FAIL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, " -> Retrieved: %s\n", ast_str_buffer(buffer));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, " -> Checking for core properties\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate(test, strstr(ast_str_buffer(buffer), "asterisk_core_properties") != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, " -> Checking for uptime\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate(test, strstr(ast_str_buffer(buffer), "asterisk_core_uptime_seconds") != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, " -> Checking for last reload\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate(test, strstr(ast_str_buffer(buffer), "asterisk_core_last_reload_seconds") != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, " -> Checking for scrape time\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate(test, strstr(ast_str_buffer(buffer), "asterisk_core_scrape_time_ms") != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_PASS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int process_config(int reload)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_config *config;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *bindaddr;</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *bindport;</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *prefix;</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *enabled;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ config = ast_config_load("http.conf", config_flags);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!config || config == CONFIG_STATUS_FILEINVALID) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(AST_LOG_NOTICE, "HTTP config file is invalid; declining load");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (config == CONFIG_STATUS_FILEUNCHANGED) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ enabled = ast_config_option(config, "general", "enabled");</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!enabled || ast_false(enabled)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_config_destroy(config);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(AST_LOG_NOTICE, "HTTP server is disabled; declining load");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Construct our Server URI */</span><br><span style="color: hsl(120, 100%, 40%);">+ bindaddr = ast_config_option(config, "general", "bindaddr");</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!bindaddr) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_config_destroy(config);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(AST_LOG_NOTICE, "HTTP config file fails to specify 'bindaddr'; declining load");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ bindport = ast_config_option(config, "general", "bindport");</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!bindport) {</span><br><span style="color: hsl(120, 100%, 40%);">+ bindport = "8088";</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ prefix = ast_config_option(config, "general", "prefix");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(server_uri, sizeof(server_uri), "http://%s:%s%s/test_metrics", bindaddr, bindport, S_OR(prefix, ""));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_config_destroy(config);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int test_init_cb(struct ast_test_info *info, struct ast_test *test)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_general_config *new_module_config;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ new_module_config = config_alloc();</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!new_module_config) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ module_config = prometheus_general_config_get();</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_general_config_set(new_module_config);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Allow the module to own the ref */</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(new_module_config, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int test_cleanup_cb(struct ast_test_info *info, struct ast_test *test)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_general_config_set(module_config);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(module_config);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int reload_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return process_config(1);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int unload_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_TEST_UNREGISTER(metric_values);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_TEST_UNREGISTER(metric_callback_register);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_TEST_UNREGISTER(metric_register);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_TEST_UNREGISTER(counter_to_string);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_TEST_UNREGISTER(counter_create);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_TEST_UNREGISTER(gauge_to_string);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_TEST_UNREGISTER(gauge_create);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_TEST_UNREGISTER(config_general_enabled);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_TEST_UNREGISTER(config_general_basic_auth);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_TEST_UNREGISTER(config_general_core_metrics);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int load_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (process_config(0)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_MODULE_LOAD_DECLINE;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_TEST_REGISTER(metric_values);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_TEST_REGISTER(metric_callback_register);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_TEST_REGISTER(metric_register);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_TEST_REGISTER(counter_to_string);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_TEST_REGISTER(counter_create);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_TEST_REGISTER(gauge_to_string);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_TEST_REGISTER(gauge_create);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_TEST_REGISTER(config_general_enabled);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_TEST_REGISTER(config_general_basic_auth);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_TEST_REGISTER(config_general_core_metrics);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_register_init(CATEGORY, &test_init_cb);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_register_cleanup(CATEGORY, &test_cleanup_cb);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_MODULE_LOAD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Prometheus Core Unit Tests",</span><br><span style="color: hsl(120, 100%, 40%);">+ .load = load_module,</span><br><span style="color: hsl(120, 100%, 40%);">+ .reload = reload_module,</span><br><span style="color: hsl(120, 100%, 40%);">+ .unload = unload_module,</span><br><span style="color: hsl(120, 100%, 40%);">+ .requires = "res_prometheus",</span><br><span style="color: hsl(120, 100%, 40%);">+);</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/11337">change 11337</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/c/asterisk/+/11337"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: I891433a272c92fd11c705a2c36d65479a415ec42 </div>
<div style="display:none"> Gerrit-Change-Number: 11337 </div>
<div style="display:none"> Gerrit-PatchSet: 8 </div>
<div style="display:none"> Gerrit-Owner: Matt Jordan <mjordan@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Friendly Automation </div>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Matt Jordan <mjordan@digium.com> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>