Asterisk - The Open Source Telephony Project GIT-master-2de1a68
pjsip_outbound_registrations.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2019 Sangoma, Inc.
5 *
6 * Matt Jordan <mjordan@digium.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19/*!
20 * \file
21 * \brief Prometheus PJSIP Outbound Registration Metrics
22 *
23 * \author Matt Jordan <mjordan@digium.com>
24 *
25 */
26
27#include "asterisk.h"
28
32
33#ifdef HAVE_PJPROJECT
34#include "asterisk/res_pjsip.h"
35#endif /* HAVE_PJPROJECT */
36
37#include "prometheus_internal.h"
38
39#ifdef HAVE_PJPROJECT
40
41/*! \internal \brief Our one and only Stasis message router */
42static struct stasis_message_router *router;
43
44/*!
45 * \internal
46 * \brief Wrapper object around our Metrics
47 *
48 * \details We keep a wrapper around the metric so we can easily
49 * update the value when the state of the registration changes, as
50 * well as remove and unregister the metric when someone destroys
51 * or reloads the registration
52 */
53struct prometheus_metric_wrapper {
54 /*!
55 * \brief The actual metric. Worth noting that we do *NOT*
56 * own the metric, as it is registered with res_prometheus.
57 * Luckily, that module doesn't destroy metrics unless we
58 * tell it to or if the module unloads.
59 */
60 struct prometheus_metric *metric;
61 /*!
62 * \brief Unique key to look up the metric
63 */
64 char key[128];
65};
66
67/*!
68 * \internal Vector of metric wrappers
69 *
70 * \details
71 * Why a vector and not an ao2_container? Two reasons:
72 * (1) There's rarely a ton of outbound registrations, so an ao2_container
73 * is overkill when we can just walk a vector
74 * (2) The lifetime of wrappers is well contained
75 */
76static AST_VECTOR(, struct prometheus_metric_wrapper *) metrics;
77
78AST_MUTEX_DEFINE_STATIC(metrics_lock);
79
80/*!
81 * \internal
82 * \brief Create a wrapper for a metric given a key
83 *
84 * \param key The unique key
85 *
86 * \retval NULL on error
87 * \retval malloc'd metric wrapper on success
88 */
89static struct prometheus_metric_wrapper *create_wrapper(const char *key)
90{
91 struct prometheus_metric_wrapper *wrapper;
92
93 wrapper = ast_calloc(1, sizeof(*wrapper));
94 if (!wrapper) {
95 return NULL;
96 }
97
98 ast_copy_string(wrapper->key, key, sizeof(wrapper->key));
99 return wrapper;
100}
101
102/*!
103 * \internal
104 * \brief Get a wrapper by its key
105 *
106 * \param key The unqiue key for the wrapper
107 *
108 * \retval NULL on no wrapper found :-\
109 * \retval wrapper on success
110 */
111static struct prometheus_metric_wrapper *get_wrapper(const char *key)
112{
113 int i;
114 SCOPED_MUTEX(lock, &metrics_lock);
115
116 for (i = 0; i < AST_VECTOR_SIZE(&metrics); i++) {
117 struct prometheus_metric_wrapper *wrapper = AST_VECTOR_GET(&metrics, i);
118
119 if (!strcmp(wrapper->key, key)) {
120 return wrapper;
121 }
122 }
123
124 return NULL;
125}
126
127/*!
128 * \internal
129 * \brief Convert an outbound registration state to a numeric value
130 *
131 * \param state The state to convert
132 *
133 * \retval int representation of the state
134 */
135static int registration_state_to_int(const char *state)
136{
137 if (!strcasecmp(state, "Registered")) {
138 return 1;
139 } else if (!strcasecmp(state, "Rejected")) {
140 return 2;
141 }
142 return 0;
143}
144
145/*!
146 * \internal
147 * \brief Sorcery observer callback called when a registration object is deleted
148 *
149 * \param obj The opaque object that was deleted
150 */
151static void registration_deleted_observer(const void *obj)
152{
153 struct ast_variable *fields;
154 struct ast_variable *it_fields;
155 int i;
156 SCOPED_MUTEX(lock, &metrics_lock);
157
158 /*
159 * Because our object is opaque, we have to do some pretty ... interesting
160 * things here to try and figure out what just happened.
161 */
163 if (!fields) {
164 ast_debug(1, "Unable to convert presumed registry object %p to strings; bailing on delete\n", obj);
165 return;
166 }
167
168 for (it_fields = fields; it_fields; it_fields = it_fields->next) {
169 if (strcasecmp(it_fields->name, "client_uri")) {
170 continue;
171 }
172
173 for (i = 0; i < AST_VECTOR_SIZE(&metrics); i++) {
174 struct prometheus_metric_wrapper *wrapper = AST_VECTOR_GET(&metrics, i);
175
176 if (strcmp(wrapper->key, it_fields->value)) {
177 continue;
178 }
179
180 ast_debug(1, "Registration metric '%s' deleted; purging with prejudice\n", wrapper->key);
182 /* This will free the metric as well */
183 prometheus_metric_unregister(wrapper->metric);
184 ast_free(wrapper);
185 }
186 }
187
188 ast_variables_destroy(fields);
189}
190
191static const struct ast_sorcery_observer registration_observer = {
193};
194
195/*!
196 * \internal
197 * \brief Sorcery observer called when an object is loaded/reloaded
198 *
199 * \param name The name of the object
200 * \param sorcery The sorcery handle
201 * \param object_type The type of object
202 * \param reloaded Whether or not we reloaded the state/definition of the object
203 *
204 * \details
205 * In our case, we only care when we re-load the registration object. We
206 * wait for the registration to occur in order to create our Prometheus
207 * metric, so we just punt on object creation. On reload, however, fundamental
208 * properties of the metric may have been changed, which means we have to remove
209 * the existing definition of the metric and allow the new registration stasis
210 * message to re-build it.
211 */
212static void registration_loaded_observer(const char *name, const struct ast_sorcery *sorcery, const char *object_type, int reloaded)
213{
214 SCOPED_MUTEX(lock, &metrics_lock);
215 int i;
216
217 if (!reloaded) {
218 /* Meh */
219 return;
220 }
221
222 if (strcmp(object_type, "registration")) {
223 /* Not interested */
224 return;
225 }
226
227 for (i = 0; i < AST_VECTOR_SIZE(&metrics); i++) {
228 struct prometheus_metric_wrapper *wrapper = AST_VECTOR_GET(&metrics, i);
229 struct ast_variable search_fields = {
230 .name = "client_uri",
231 .value = wrapper->key,
232 .next = NULL,
233 };
234 void *obj;
235
236 ast_debug(1, "Checking for the existance of registration metric %s\n", wrapper->key);
238 if (!obj) {
239 ast_debug(1, "Registration metric '%s' not found; purging with prejudice\n", wrapper->key);
241 /* This will free the metric as well */
242 prometheus_metric_unregister(wrapper->metric);
243 ast_free(wrapper);
244 continue;
245 }
246 ao2_ref(obj, -1);
247 }
248
249}
250
253};
254
255/*!
256 * \internal
257 * \brief Callback for Stasis Registry messages
258 *
259 * \param data Callback data, always NULL
260 * \param sub Stasis subscription
261 * \param message Our Registry message
262 *
263 * \details
264 * The Stasis Registry message both updates the state of the Prometheus metric
265 * as well as forces its creation.
266 */
267static void registry_message_cb(void *data, struct stasis_subscription *sub,
268 struct stasis_message *message)
269{
271 struct ast_json *json = payload->json;
272 const char *username = ast_json_string_get(ast_json_object_get(json, "username"));
273 const char *status_str = ast_json_string_get(ast_json_object_get(json, "status"));
274 const char *domain = ast_json_string_get(ast_json_object_get(json, "domain"));
275 const char *channel_type = ast_json_string_get(ast_json_object_get(json, "channeltype"));
278 "asterisk_pjsip_outbound_registration_status",
279 "Current registration status. 0=Unregistered; 1=Registered; 2=Rejected.",
280 NULL
281 );
282 struct prometheus_metric_wrapper *wrapper;
283 char eid_str[32];
284
285 ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
286
287 PROMETHEUS_METRIC_SET_LABEL(&metric, 0, "eid", eid_str);
288 PROMETHEUS_METRIC_SET_LABEL(&metric, 1, "username", username);
289 PROMETHEUS_METRIC_SET_LABEL(&metric, 2, "domain", domain);
290 PROMETHEUS_METRIC_SET_LABEL(&metric, 3, "channel_type", channel_type);
291 snprintf(metric.value, sizeof(metric.value), "%d", registration_state_to_int(status_str));
292
293 wrapper = get_wrapper(username);
294 if (wrapper) {
295 ast_mutex_lock(&wrapper->metric->lock);
296 /* Safe */
297 strcpy(wrapper->metric->value, metric.value);
298 ast_mutex_unlock(&wrapper->metric->lock);
299 } else {
300 wrapper = create_wrapper(username);
301 if (!wrapper) {
302 return;
303 }
304
305 wrapper->metric = prometheus_gauge_create(metric.name, metric.help);
306 if (!wrapper->metric) {
307 ast_free(wrapper);
308 return;
309 }
310 *(wrapper->metric) = metric;
311
312 prometheus_metric_register(wrapper->metric);
313 AST_VECTOR_APPEND(&metrics, wrapper);
314 }
315}
316
317#endif /* HAVE_PJPROJECT */
318
319/*!
320 * \internal
321 * \brief Callback invoked when the core module is unloaded
322 */
324{
325#ifdef HAVE_PJPROJECT
327 router = NULL;
330#endif /* HAVE_PJPROJECT */
331}
332
333/*!
334 * \internal
335 * \brief Metrics provider definition
336 */
338 .name = "pjsip_outbound_registration",
340};
341
343{
345
346#ifdef HAVE_PJPROJECT
348 if (!router) {
349 goto cleanup;
350 }
351
352 if (stasis_message_router_add(router, ast_system_registry_type(), registry_message_cb, NULL)) {
353 goto cleanup;
354 }
355
357 goto cleanup;
358 }
359
361 goto cleanup;
362 }
363#endif /* HAVE_PJPROJECT */
364 return 0;
365
366#ifdef HAVE_PJPROJECT
367cleanup:
369 router = NULL;
372
373 return -1;
374#endif /* HAVE_PJPROJECT */
375}
ast_mutex_t lock
Definition: app_sla.c:331
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:180
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
static const char channel_type[]
Definition: chan_motif.c:321
static const char name[]
Definition: format_mp3.c:68
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
#define ast_debug(level,...)
Log a DEBUG message.
const char * ast_json_string_get(const struct ast_json *string)
Get the value of a JSON string.
Definition: json.c:283
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
Definition: json.c:407
#define ast_mutex_unlock(a)
Definition: lock.h:190
#define SCOPED_MUTEX(varname, lock)
scoped lock specialization for mutexes
Definition: lock.h:589
#define ast_mutex_lock(a)
Definition: lock.h:189
#define AST_MUTEX_DEFINE_STATIC(mutex)
Definition: lock.h:520
static void * cleanup(void *unused)
Definition: pbx_realtime.c:124
int pjsip_outbound_registration_metrics_init(void)
Initialize PJSIP outbound registration metrics.
static struct prometheus_metrics_provider provider
static void pjsip_outbound_registration_metrics_unload_cb(void)
Prometheus Metric Internal API.
static struct stasis_message_router * router
struct stasis_forward * sub
Definition: res_corosync.c:240
struct ast_sorcery * ast_sip_get_sorcery(void)
Get a pointer to the SIP sorcery structure.
static const struct ast_sorcery_instance_observer observer_callbacks_registrations
static void registration_deleted_observer(const void *obj)
static const struct ast_sorcery_observer registration_observer
static void registration_loaded_observer(const char *name, const struct ast_sorcery *sorcery, const char *object_type, int reloaded)
static struct ast_sorcery * sorcery
struct @467 metrics
Asterisk Prometheus Metrics.
int prometheus_metric_unregister(struct prometheus_metric *metric)
Remove a registered metric.
#define PROMETHEUS_METRIC_SET_LABEL(metric, label, n, v)
Convenience macro for setting a label / value in a metric.
int prometheus_metric_register(struct prometheus_metric *metric)
#define PROMETHEUS_METRIC_STATIC_INITIALIZATION(mtype, n, h, cb)
Convenience macro for initializing a metric on the stack.
@ PROMETHEUS_METRIC_GAUGE
A metric whose value can bounce around like a jackrabbit.
struct prometheus_metric * prometheus_gauge_create(const char *name, const char *help)
Create a malloc'd gauge metric.
void prometheus_metrics_provider_register(const struct prometheus_metrics_provider *provider)
Register a metrics provider.
#define NULL
Definition: resample.c:96
void ast_sorcery_observer_remove(const struct ast_sorcery *sorcery, const char *type, const struct ast_sorcery_observer *callbacks)
Remove an observer from a specific object type.
Definition: sorcery.c:2418
#define ast_sorcery_objectset_create(sorcery, object)
Create an object set (KVP list) for an object.
Definition: sorcery.h:1137
void ast_sorcery_instance_observer_remove(struct ast_sorcery *sorcery, const struct ast_sorcery_instance_observer *callbacks)
Remove an observer from a sorcery instance.
Definition: sorcery.c:537
@ AST_RETRIEVE_FLAG_DEFAULT
Default retrieval flags.
Definition: sorcery.h:117
int ast_sorcery_observer_add(const struct ast_sorcery *sorcery, const char *type, const struct ast_sorcery_observer *callbacks)
Add an observer to a specific object type.
Definition: sorcery.c:2386
int ast_sorcery_instance_observer_add(struct ast_sorcery *sorcery, const struct ast_sorcery_instance_observer *callbacks)
Add an observer to a sorcery instance.
Definition: sorcery.c:520
void * ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const char *type, unsigned int flags, struct ast_variable *fields)
Retrieve an object or multiple objects using specific fields.
Definition: sorcery.c:1897
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
#define stasis_message_router_create(topic)
Create a new message router object.
int stasis_message_router_add(struct stasis_message_router *router, struct stasis_message_type *message_type, stasis_subscription_cb callback, void *data)
Add a route to a message router.
void stasis_message_router_unsubscribe_and_join(struct stasis_message_router *router)
Unsubscribe the router from the upstream topic, blocking until the final message has been processed.
struct stasis_message_type * ast_system_registry_type(void)
A stasis_message_type for outbound registration.
struct stasis_topic * ast_system_topic(void)
A Stasis Message Bus API topic which publishes messages regarding system changes.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
struct ast_json * json
Definition: json.h:1083
Abstract JSON element (object, array, string, int, ...).
Interface for the sorcery instance observer.
Definition: sorcery.h:237
void(* object_type_loaded)(const char *name, const struct ast_sorcery *sorcery, const char *object_type, int reloaded)
Callback after any object_type is loaded/reloaded.
Definition: sorcery.h:260
Interface for a sorcery object type observer.
Definition: sorcery.h:332
void(* deleted)(const void *object)
Callback for when an object is deleted.
Definition: sorcery.h:340
Full structure for sorcery.
Definition: sorcery.c:230
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next
An actual, honest to god, metric.
char name[PROMETHEUS_MAX_NAME_LENGTH]
Our metric name.
const char * help
Pointer to a static string defining this metric's help text.
char value[PROMETHEUS_MAX_VALUE_LENGTH]
The current value.
A function table for a metrics provider.
const char * name
Handy name of the provider for debugging purposes.
char * ast_eid_to_str(char *s, int maxlen, struct ast_eid *eid)
Convert an EID to a string.
Definition: utils.c:2839
struct ast_eid ast_eid_default
Global EID.
Definition: options.c:93
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:609
#define AST_VECTOR_REMOVE(vec, idx, preserve_ordered)
Remove an element from a vector by index.
Definition: vector.h:412
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition: vector.h:256
#define AST_VECTOR(name, type)
Define a vector structure.
Definition: vector.h:44
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:680