<p>Matt Jordan has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/11372">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">res_prometheus: Add metrics for PJSIP outbound registrations<br><br>When monitoring Asterisk instances, it's often useful to know when an<br>outbound registration fails, as this often maps to the notion of a trunk<br>and having a trunk fail is usually a "bad thing". As such, this patch<br>adds monitoring metrics that track the state of PJSIP outbound registrations.<br>It does this by looking for the Registry events coming across the Stasis<br>system topic, and publishing those as metrics to Prometheus. Note that<br>while this may support other outbound registration types (IAX2, SIP, etc.)<br>those haven't been tested. Your mileage may vary.<br><br>(And why are you still using IAX2 and SIP? It's 2019 folks. Get with the<br>program.)<br><br>This patch also adds Sorcery observers to handle modifications to the<br>underlying PJSIP outbound registration objects. This is useful when a<br>reload is triggered that modifies the properties of an outbound registration,<br>or when ARI push configuration is used and an object is updated or<br>deleted. Because we rely on properties of the registration object to<br>define the metric (label key/value pairs), we delete the relevant metric when<br>we notice that something has changed and wait for a new Stasis message to<br>arrive to re-create the metric.<br><br>ASTERISK-28403<br><br>Change-Id: If01420e38530fc20b6dd4aa15cd281d94cd2b87e<br>---<br>A res/prometheus/pjsip_outbound_registrations.c<br>M res/prometheus/prometheus_internal.h<br>M res/res_prometheus.c<br>3 files changed, 394 insertions(+), 1 deletion(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/72/11372/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/res/prometheus/pjsip_outbound_registrations.c b/res/prometheus/pjsip_outbound_registrations.c</span><br><span>new file mode 100644</span><br><span>index 0000000..2b6d21b</span><br><span>--- /dev/null</span><br><span>+++ b/res/prometheus/pjsip_outbound_registrations.c</span><br><span>@@ -0,0 +1,383 @@</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 Prometheus PJSIP Outbound Registration Metrics</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%);">+#include "asterisk.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/stasis_message_router.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/stasis_system.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%);">+#ifdef HAVE_PJPROJECT</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_pjsip.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#endif /* HAVE_PJPROJECT */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "prometheus_internal.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef HAVE_PJPROJECT</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \internal \brief Our one and only Stasis message router */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct stasis_message_router *router;</span><br><span 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 Wrapper object around our Metrics</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \details We keep a wrapper around the metric so we can easily</span><br><span style="color: hsl(120, 100%, 40%);">+ * update the value when the state of the registration changes, as</span><br><span style="color: hsl(120, 100%, 40%);">+ * well as remove and unregsiter the metric when someone destroys</span><br><span style="color: hsl(120, 100%, 40%);">+ * or reloads the registration</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct prometheus_metric_wrapper {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief The actual metric. Worth noting that we do *NOT*</span><br><span style="color: hsl(120, 100%, 40%);">+ * own the metric, as it is registered with res_prometheus.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Luckily, that module doesn't destroy metrics unless we</span><br><span style="color: hsl(120, 100%, 40%);">+ * tell it to or if the module unloads.</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%);">+ * \brief Unique key to look up the metric</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ char key[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%);">+AST_MUTEX_DEFINE_STATIC(metrics_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%);">+ * \internal Vector of metric wrappers</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%);">+ * Why a vector and not an ao2_container? Two reasons:</span><br><span style="color: hsl(120, 100%, 40%);">+ * (1) There's rarely a ton of outbound registrations, so an ao2_container</span><br><span style="color: hsl(120, 100%, 40%);">+ * is overkill when we can just walk a vector</span><br><span style="color: hsl(120, 100%, 40%);">+ * (2) The lifetime of wrappers is well contained</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static AST_VECTOR(, struct prometheus_metric_wrapper *) 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%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Create a wrapper for a metric given a key</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param key The unique key</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 malloc'd metric wrapper on success</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct prometheus_metric_wrapper *create_wrapper(const char *key)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_metric_wrapper *wrapper;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ wrapper = ast_calloc(1, sizeof(*wrapper));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!wrapper) {</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%);">+ ast_copy_string(wrapper->key, key, sizeof(wrapper->key));</span><br><span style="color: hsl(120, 100%, 40%);">+ return wrapper;</span><br><span 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 Get a wrapper by its key</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param key The unqiue key for the wrapper</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval NULL on no wrapper found :-\</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval wrapper on success</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct prometheus_metric_wrapper *get_wrapper(const char *key)</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%);">+ SCOPED_MUTEX(lock, &metrics_lock);</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_wrapper *wrapper = AST_VECTOR_GET(&metrics, i);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!wrapper) {</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span 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(wrapper->key, key)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return wrapper;</span><br><span 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 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%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Convert an outbound registration state to a numeric value</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param state The state to convert</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval int representation of the state</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int registration_state_to_int(const char *state)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!strcasecmp(state, "Registered")) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (!strcasecmp(state, "Rejected")) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return 2;</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%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Sorcery observer callback called when a registration object is deleted</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param obj The opaque object that was deleted</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static void registration_deleted_observer(const void *obj)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_variable *fields;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_variable *it_fields;</span><br><span style="color: hsl(120, 100%, 40%);">+ int i;</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPED_MUTEX(lock, &metrics_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%);">+ * Because our object is opaque, we have to do some pretty ... interesting</span><br><span style="color: hsl(120, 100%, 40%);">+ * things here to try and figure out what just happened.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ fields = ast_sorcery_objectset_create(ast_sip_get_sorcery(), obj);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!fields) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "Unable to convert presumed registry object %p to strings; bailing on delete\n", obj);</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%);">+ for (it_fields = fields; it_fields; it_fields = it_fields->next) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcasecmp(it_fields->name, "client_uri")) {</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span 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_wrapper *wrapper = AST_VECTOR_GET(&metrics, i);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!wrapper) {</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span 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(wrapper->key, it_fields->value)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span 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(1, "Registration metric '%s' deleted; purging with prejudice\n", wrapper->key);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_REMOVE(&metrics, i, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ /* This will free the metric as well */</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_metric_unregister(wrapper->metric);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(wrapper);</span><br><span 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_variables_destroy(fields);</span><br><span 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 struct ast_sorcery_observer registration_observer = {</span><br><span style="color: hsl(120, 100%, 40%);">+ .deleted = registration_deleted_observer,</span><br><span 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 Sorcery observer called when an object is loaded/reloaded</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param name The name of the object</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param sorcery The sorcery handle</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param object_type The type of object</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param reloaded Whether or not we reloaded the state/definition of the 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%);">+ * In our case, we only care when we re-load the registration object. We</span><br><span style="color: hsl(120, 100%, 40%);">+ * wait for the registration to occur in order to create our Prometheus</span><br><span style="color: hsl(120, 100%, 40%);">+ * metric, so we just punt on object creation. On reload, however, fundamental</span><br><span style="color: hsl(120, 100%, 40%);">+ * properties of the metric may have been changed, which means we have to remove</span><br><span style="color: hsl(120, 100%, 40%);">+ * the existing definition of the metric and allow the new registration stasis</span><br><span style="color: hsl(120, 100%, 40%);">+ * message to re-build it.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static void registration_loaded_observer(const char *name, const struct ast_sorcery *sorcery, const char *object_type, int reloaded)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPED_MUTEX(lock, &metrics_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 (!reloaded) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Meh */</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%);">+ if (strcmp(object_type, "registration")) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Not interested */</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%);">+ for (i = 0; i < AST_VECTOR_SIZE(&metrics); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_metric_wrapper *wrapper = AST_VECTOR_GET(&metrics, i);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_variable search_fields = {</span><br><span style="color: hsl(120, 100%, 40%);">+ .name = "client_uri",</span><br><span style="color: hsl(120, 100%, 40%);">+ .value = wrapper->key,</span><br><span style="color: hsl(120, 100%, 40%);">+ .next = NULL,</span><br><span style="color: hsl(120, 100%, 40%);">+ };</span><br><span style="color: hsl(120, 100%, 40%);">+ void *obj;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "Checking for the existance of registration metric %s\n", wrapper->key);</span><br><span style="color: hsl(120, 100%, 40%);">+ obj = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), object_type, AST_RETRIEVE_FLAG_DEFAULT, &search_fields);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!obj) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "Registration metric '%s' not found; purging with prejudice\n", wrapper->key);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_REMOVE(&metrics, i, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ /* This will free the metric as well */</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_metric_unregister(wrapper->metric);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(wrapper);</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(obj, -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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const struct ast_sorcery_instance_observer observer_callbacks_registrations = {</span><br><span style="color: hsl(120, 100%, 40%);">+ .object_type_loaded = registration_loaded_observer,</span><br><span 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 Callback for Stasis Registry messages</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param data Callback data, always NULL</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param sub Stasis subscription</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param message Our Registry message</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 Stasis Registry message both updates the state of the Prometheus metric</span><br><span style="color: hsl(120, 100%, 40%);">+ * as well as forces its creation.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static void registry_message_cb(void *data, struct stasis_subscription *sub,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct stasis_message *message)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_json_payload *payload = stasis_message_data(message);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_json *json = payload->json;</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *username = ast_json_string_get(ast_json_object_get(json, "username"));</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *status_str = ast_json_string_get(ast_json_object_get(json, "status"));</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *domain = ast_json_string_get(ast_json_object_get(json, "domain"));</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *channel_type = ast_json_string_get(ast_json_object_get(json, "channeltype"));</span><br><span style="color: hsl(120, 100%, 40%);">+ struct prometheus_metric metric = 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%);">+ "asterisk_pjsip_outbound_registration_status",</span><br><span style="color: hsl(120, 100%, 40%);">+ "Current registration status. 0=Unregistered; 1=Registered; 2=Rejected.",</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%);">+ struct prometheus_metric_wrapper *wrapper;</span><br><span style="color: hsl(120, 100%, 40%);">+ char eid_str[32];</span><br><span style="color: hsl(120, 100%, 40%);">+</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(&metric, 0, "eid", eid_str);</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_SET_LABEL(&metric, 1, "username", username);</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_SET_LABEL(&metric, 2, "domain", domain);</span><br><span style="color: hsl(120, 100%, 40%);">+ PROMETHEUS_METRIC_SET_LABEL(&metric, 3, "channel_type", channel_type);</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(metric.value, sizeof(metric.value), "%d", registration_state_to_int(status_str));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ wrapper = get_wrapper(username);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (wrapper) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_mutex_lock(&wrapper->metric->lock);</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Safe */</span><br><span style="color: hsl(120, 100%, 40%);">+ strcpy(wrapper->metric->value, metric.value);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_mutex_unlock(&wrapper->metric->lock);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ wrapper = create_wrapper(username);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!wrapper) {</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%);">+ wrapper->metric = prometheus_gauge_create(metric.name, metric.help);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!wrapper->metric) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(wrapper);</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%);">+ *(wrapper->metric) = metric;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_metric_register(wrapper->metric);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_APPEND(&metrics, wrapper);</span><br><span 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%);">+#endif /* HAVE_PJPROJECT */</span><br><span 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 Callback invoked when the core module is unloaded</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static void pjsip_outbound_registration_metrics_unload_cb(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef HAVE_PJPROJECT</span><br><span style="color: hsl(120, 100%, 40%);">+ stasis_message_router_unsubscribe_and_join(router);</span><br><span style="color: hsl(120, 100%, 40%);">+ router = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sorcery_instance_observer_remove(ast_sip_get_sorcery(), &observer_callbacks_registrations);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sorcery_observer_remove(ast_sip_get_sorcery(), "registration", ®istration_observer);</span><br><span style="color: hsl(120, 100%, 40%);">+#endif /* HAVE_PJPROJECT */</span><br><span 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 Metrics provider definition</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct prometheus_metrics_provider provider = {</span><br><span style="color: hsl(120, 100%, 40%);">+ .name = "pjsip_outbound_registration",</span><br><span style="color: hsl(120, 100%, 40%);">+ .unload_cb = pjsip_outbound_registration_metrics_unload_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%);">+int pjsip_outbound_registration_metrics_init(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ prometheus_metrics_provider_register(&provider);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef HAVE_PJPROJECT</span><br><span style="color: hsl(120, 100%, 40%);">+ router = stasis_message_router_create(ast_system_topic());</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!router) {</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 (stasis_message_router_add(router, ast_system_registry_type(), registry_message_cb, NULL)) {</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_sorcery_instance_observer_add(ast_sip_get_sorcery(), &observer_callbacks_registrations)) {</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_sorcery_observer_add(ast_sip_get_sorcery(), "registration", ®istration_observer)) {</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%);">+#endif /* HAVE_PJPROJECT */</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%);">+#ifdef HAVE_PJPROJECT</span><br><span style="color: hsl(120, 100%, 40%);">+cleanup:</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(router);</span><br><span style="color: hsl(120, 100%, 40%);">+ router = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sorcery_instance_observer_remove(ast_sip_get_sorcery(), &observer_callbacks_registrations);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sorcery_observer_remove(ast_sip_get_sorcery(), "registration", ®istration_observer);</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%);">+#endif /* HAVE_PJPROJECT */</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/res/prometheus/prometheus_internal.h b/res/prometheus/prometheus_internal.h</span><br><span>index 3b3618a..ad6c528 100644</span><br><span>--- a/res/prometheus/prometheus_internal.h</span><br><span>+++ b/res/prometheus/prometheus_internal.h</span><br><span>@@ -91,4 +91,12 @@</span><br><span> */</span><br><span> int bridge_metrics_init(void);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Initialize PJSIP outbound registration metrics</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 pjsip_outbound_registration_metrics_init(void);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #endif /* #define PROMETHEUS_INTERNAL_H__ */</span><br><span>diff --git a/res/res_prometheus.c b/res/res_prometheus.c</span><br><span>index 534e70b..37f5b82 100644</span><br><span>--- a/res/res_prometheus.c</span><br><span>+++ b/res/res_prometheus.c</span><br><span>@@ -25,6 +25,7 @@</span><br><span> */</span><br><span> </span><br><span> /*** MODULEINFO</span><br><span style="color: hsl(120, 100%, 40%);">+ <use>pjproject</use></span><br><span> <support_level>extended</support_level></span><br><span> ***/</span><br><span> </span><br><span>@@ -965,7 +966,8 @@</span><br><span> if (cli_init()</span><br><span> || channel_metrics_init()</span><br><span> || endpoint_metrics_init()</span><br><span style="color: hsl(0, 100%, 40%);">- || bridge_metrics_init()) {</span><br><span style="color: hsl(120, 100%, 40%);">+ || bridge_metrics_init()</span><br><span style="color: hsl(120, 100%, 40%);">+ || pjsip_outbound_registration_metrics_init()) {</span><br><span> goto cleanup;</span><br><span> }</span><br><span> </span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/11372">change 11372</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/+/11372"/><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: If01420e38530fc20b6dd4aa15cd281d94cd2b87e </div>
<div style="display:none"> Gerrit-Change-Number: 11372 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Matt Jordan <mjordan@digium.com> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>