<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>