Merge "res/stasis: Add CLI commands for displaying/debugging ARI apps"

This commit is contained in:
Joshua Colp 2016-11-01 16:05:03 -05:00 committed by Gerrit Code Review
commit a2dde4a543
8 changed files with 411 additions and 0 deletions

12
CHANGES
View File

@ -69,6 +69,18 @@ chan_pjsip
to asymmetric using the "asymmetric_rtp_codec" endpoint option. If this to asymmetric using the "asymmetric_rtp_codec" endpoint option. If this
option is set then the sending and received codec are allowed to differ. option is set then the sending and received codec are allowed to differ.
CLI Commands
------------------
* Three new CLI commands have been added for ARI:
- ari show apps:
Displays a listing of all registered ARI applications.
- ari show app <name>:
Display detailed information about a registered ARI application.
- ari set debug <name> <on|off>:
Enable/disable debugging of an ARI application. When debugged, verbose
information will be sent to the Asterisk CLI.
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
--- Functionality changes from Asterisk 14.0.0 to Asterisk 14.1.0 ---------- --- Functionality changes from Asterisk 14.0.0 to Asterisk 14.1.0 ----------
------------------------------------------------------------------------------ ------------------------------------------------------------------------------

View File

@ -76,6 +76,16 @@ typedef void (*stasis_app_cb)(void *data, const char *app_name,
*/ */
struct ao2_container *stasis_app_get_all(void); struct ao2_container *stasis_app_get_all(void);
/*!
* \brief Retrieve a handle to a Stasis application by its name
*
* \param name The name of the registered Stasis application
*
* \return \c stasis_app on success.
* \return \c NULL on error.
*/
struct stasis_app *stasis_app_get_by_name(const char *name);
/*! /*!
* \brief Register a new Stasis application. * \brief Register a new Stasis application.
* *

View File

@ -172,7 +172,9 @@ int ast_ari_websocket_session_write(struct ast_ari_websocket_session *session,
return -1; return -1;
} }
#ifdef AST_DEVMODE
ast_debug(3, "Examining ARI event (length %u): \n%s\n", (unsigned int) strlen(str), str); ast_debug(3, "Examining ARI event (length %u): \n%s\n", (unsigned int) strlen(str), str);
#endif
if (ast_websocket_write_string(session->ws_session, str)) { if (ast_websocket_write_string(session->ws_session, str)) {
ast_log(LOG_NOTICE, "Problem occurred during websocket write, websocket closed\n"); ast_log(LOG_NOTICE, "Problem occurred during websocket write, websocket closed\n");
return -1; return -1;

View File

@ -65,6 +65,7 @@
#include "stasis/app.h" #include "stasis/app.h"
#include "stasis/control.h" #include "stasis/control.h"
#include "stasis/messaging.h" #include "stasis/messaging.h"
#include "stasis/cli.h"
#include "stasis/stasis_bridge.h" #include "stasis/stasis_bridge.h"
#include "asterisk/core_unreal.h" #include "asterisk/core_unreal.h"
#include "asterisk/musiconhold.h" #include "asterisk/musiconhold.h"
@ -1487,6 +1488,11 @@ static struct stasis_app *find_app_by_name(const char *app_name)
return res; return res;
} }
struct stasis_app *stasis_app_get_by_name(const char *name)
{
return find_app_by_name(name);
}
static int append_name(void *obj, void *arg, int flags) static int append_name(void *obj, void *arg, int flags)
{ {
struct stasis_app *app = obj; struct stasis_app *app = obj;
@ -1959,6 +1965,8 @@ static int unload_module(void)
{ {
stasis_app_unregister_event_sources(); stasis_app_unregister_event_sources();
cli_cleanup();
messaging_cleanup(); messaging_cleanup();
cleanup(); cleanup();
@ -2118,6 +2126,11 @@ static int load_module(void)
return AST_MODULE_LOAD_FAILURE; return AST_MODULE_LOAD_FAILURE;
} }
if (cli_init()) {
unload_module();
return AST_MODULE_LOAD_FAILURE;
}
bridge_stasis_init(); bridge_stasis_init();
stasis_app_register_event_sources(); stasis_app_register_event_sources();

View File

@ -30,6 +30,7 @@
#include "messaging.h" #include "messaging.h"
#include "asterisk/callerid.h" #include "asterisk/callerid.h"
#include "asterisk/cli.h"
#include "asterisk/stasis_app.h" #include "asterisk/stasis_app.h"
#include "asterisk/stasis_bridges.h" #include "asterisk/stasis_bridges.h"
#include "asterisk/stasis_channels.h" #include "asterisk/stasis_channels.h"
@ -59,6 +60,8 @@ struct stasis_app {
void *data; void *data;
/*! Subscription model for the application */ /*! Subscription model for the application */
enum stasis_app_subscription_model subscription_model; enum stasis_app_subscription_model subscription_model;
/*! Whether or not someone wants to see debug messages about this app */
int debug;
/*! Name of the Stasis application */ /*! Name of the Stasis application */
char name[]; char name[];
}; };
@ -830,6 +833,18 @@ static void bridge_default_handler(void *data, struct stasis_subscription *sub,
} }
} }
void app_set_debug(struct stasis_app *app, int debug)
{
if (!app) {
return;
}
{
SCOPED_AO2LOCK(lock, app);
app->debug = debug;
}
}
struct stasis_app *app_create(const char *name, stasis_app_cb handler, void *data, enum stasis_app_subscription_model subscription_model) struct stasis_app *app_create(const char *name, stasis_app_cb handler, void *data, enum stasis_app_subscription_model subscription_model)
{ {
RAII_VAR(struct stasis_app *, app, NULL, ao2_cleanup); RAII_VAR(struct stasis_app *, app, NULL, ao2_cleanup);
@ -927,6 +942,7 @@ struct stasis_topic *ast_app_get_topic(struct stasis_app *app)
void app_send(struct stasis_app *app, struct ast_json *message) void app_send(struct stasis_app *app, struct ast_json *message)
{ {
stasis_app_cb handler; stasis_app_cb handler;
int debug;
char eid[20]; char eid[20];
RAII_VAR(void *, data, NULL, ao2_cleanup); RAII_VAR(void *, data, NULL, ao2_cleanup);
@ -939,6 +955,7 @@ void app_send(struct stasis_app *app, struct ast_json *message)
/* Copy off mutable state with lock held */ /* Copy off mutable state with lock held */
{ {
SCOPED_AO2LOCK(lock, app); SCOPED_AO2LOCK(lock, app);
debug = app->debug;
handler = app->handler; handler = app->handler;
if (app->data) { if (app->data) {
ao2_ref(app->data, +1); ao2_ref(app->data, +1);
@ -947,6 +964,13 @@ void app_send(struct stasis_app *app, struct ast_json *message)
/* Name is immutable; no need to copy */ /* Name is immutable; no need to copy */
} }
if (debug) {
char *dump = ast_json_dump_string_format(message, AST_JSON_PRETTY);
ast_verb(0, "Dispatching message to Stasis app '%s':\n%s\n",
app->name, dump);
ast_json_free(dump);
}
if (!handler) { if (!handler) {
ast_verb(3, ast_verb(3,
"Inactive Stasis app '%s' missed message\n", app->name); "Inactive Stasis app '%s' missed message\n", app->name);
@ -1024,6 +1048,73 @@ const char *app_name(const struct stasis_app *app)
return app->name; return app->name;
} }
static int forwards_filter_by_type(void *obj, void *arg, int flags)
{
struct app_forwards *forward = obj;
enum forward_type *forward_type = arg;
if (forward->forward_type == *forward_type) {
return CMP_MATCH;
}
return 0;
}
void app_to_cli(const struct stasis_app *app, struct ast_cli_args *a)
{
struct ao2_iterator *channels;
struct ao2_iterator *endpoints;
struct ao2_iterator *bridges;
struct app_forwards *forward;
enum forward_type forward_type;
ast_cli(a->fd, "Name: %s\n"
" Debug: %s\n"
" Subscription Model: %s\n",
app->name,
app->debug ? "Yes" : "No",
app->subscription_model == STASIS_APP_SUBSCRIBE_ALL ?
"Global Resource Subscription" :
"Application/Explicit Resource Subscription");
ast_cli(a->fd, " Subscriptions: %d\n", ao2_container_count(app->forwards));
ast_cli(a->fd, " Channels:\n");
forward_type = FORWARD_CHANNEL;
channels = ao2_callback(app->forwards, OBJ_MULTIPLE,
forwards_filter_by_type, &forward_type);
if (channels) {
while ((forward = ao2_iterator_next(channels))) {
ast_cli(a->fd, " %s (%d)\n", forward->id, forward->interested);
ao2_ref(forward, -1);
}
ao2_iterator_destroy(channels);
}
ast_cli(a->fd, " Bridges:\n");
forward_type = FORWARD_BRIDGE;
bridges = ao2_callback(app->forwards, OBJ_MULTIPLE,
forwards_filter_by_type, &forward_type);
if (bridges) {
while ((forward = ao2_iterator_next(bridges))) {
ast_cli(a->fd, " %s (%d)\n", forward->id, forward->interested);
ao2_ref(forward, -1);
}
ao2_iterator_destroy(bridges);
}
ast_cli(a->fd, " Endpoints:\n");
forward_type = FORWARD_ENDPOINT;
endpoints = ao2_callback(app->forwards, OBJ_MULTIPLE,
forwards_filter_by_type, &forward_type);
if (endpoints) {
while ((forward = ao2_iterator_next(endpoints))) {
ast_cli(a->fd, " %s (%d)\n", forward->id, forward->interested);
ao2_ref(forward, -1);
}
ao2_iterator_destroy(endpoints);
}
}
struct ast_json *app_to_json(const struct stasis_app *app) struct ast_json *app_to_json(const struct stasis_app *app)
{ {
RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);

View File

@ -127,8 +127,26 @@ void app_send(struct stasis_app *app, struct ast_json *message);
struct app_forwards; struct app_forwards;
/*!
* \brief Create a JSON representation of a \c stasis_app
*
* \param app The application
*
* \return \c JSON blob on success
* \return \c NULL on error
*/
struct ast_json *app_to_json(const struct stasis_app *app); struct ast_json *app_to_json(const struct stasis_app *app);
struct ast_cli_args;
/*!
* \brief Dump properties of a \c stasis_app to the CLI
*
* \param app The application
* \param a The CLI arguments
*/
void app_to_cli(const struct stasis_app *app, struct ast_cli_args *a);
/*! /*!
* \brief Subscribes an application to a channel. * \brief Subscribes an application to a channel.
* *
@ -282,4 +300,12 @@ char *app_get_replace_channel_app(struct ast_channel *chan);
*/ */
int app_send_end_msg(struct stasis_app *app, struct ast_channel *chan); int app_send_end_msg(struct stasis_app *app, struct ast_channel *chan);
/*!
* \brief Enable/disable debugging on an application
*
* \param app The app to debug
* \param debug If non-zero, enable debugging. If zero, disable.
*/
void app_set_debug(struct stasis_app *app, int debug);
#endif /* _ASTERISK_RES_STASIS_APP_H */ #endif /* _ASTERISK_RES_STASIS_APP_H */

214
res/stasis/cli.c Normal file
View File

@ -0,0 +1,214 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2016, Digium, Inc.
*
* Matt Jordan <mjordan@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
*
* \brief Stasis CLI commands.
*
* \author Matt Jordan <mjordan@digium.com>
*/
#include "asterisk.h"
#include "asterisk/cli.h"
#include "asterisk/astobj2.h"
#include "cli.h"
#include "app.h"
static char *ari_show_apps(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct ao2_container *apps;
struct ao2_iterator it_apps;
char *app;
switch (cmd) {
case CLI_INIT:
e->command = "ari show apps";
e->usage =
"Usage: ari show apps\n"
" Lists all registered applications.\n"
;
return NULL;
case CLI_GENERATE:
return NULL;
default:
break;
}
if (a->argc != 3) {
return CLI_SHOWUSAGE;
}
apps = stasis_app_get_all();
if (!apps) {
ast_cli(a->fd, "Unable to retrieve registered applications!\n");
return CLI_FAILURE;
}
ast_cli(a->fd, "Application Name \n");
ast_cli(a->fd, "=========================\n");
it_apps = ao2_iterator_init(apps, 0);
while ((app = ao2_iterator_next(&it_apps))) {
ast_cli(a->fd, "%-25.25s\n", app);
ao2_ref(app, -1);
}
ao2_iterator_destroy(&it_apps);
ao2_ref(apps, -1);
return CLI_SUCCESS;
}
struct app_complete {
/*! Nth app to search for */
int state;
/*! Which app currently on */
int which;
};
static int complete_ari_app_search(void *obj, void *arg, void *data, int flags)
{
struct app_complete *search = data;
if (++search->which > search->state) {
return CMP_MATCH;
}
return 0;
}
static char *complete_ari_app(struct ast_cli_args *a)
{
RAII_VAR(struct ao2_container *, apps, stasis_app_get_all(), ao2_cleanup);
RAII_VAR(char *, app, NULL, ao2_cleanup);
struct app_complete search = {
.state = a->n,
};
if (!apps) {
ast_cli(a->fd, "Error getting ARI applications\n");
return CLI_FAILURE;
}
app = ao2_callback_data(apps,
ast_strlen_zero(a->word) ? 0 : OBJ_PARTIAL_KEY,
complete_ari_app_search, (char*)a->word, &search);
return app ? ast_strdup(app) : NULL;
}
static char *complete_ari_show_app(struct ast_cli_args *a)
{
if (a->pos == 3) {
return complete_ari_app(a);
}
return NULL;
}
static char *ari_show_app(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
void *app;
switch (cmd) {
case CLI_INIT:
e->command = "ari show app";
e->usage =
"Usage: ari show app <application>\n"
" Provide detailed information about a registered application.\n"
;
return NULL;
case CLI_GENERATE:
return complete_ari_show_app(a);
default:
break;
}
if (a->argc != 4) {
return CLI_SHOWUSAGE;
}
app = stasis_app_get_by_name(a->argv[3]);
if (!app) {
return CLI_FAILURE;
}
app_to_cli(app, a);
ao2_ref(app, -1);
return CLI_SUCCESS;
}
static char *ari_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
void *app;
int debug;
switch (cmd) {
case CLI_INIT:
e->command = "ari set debug";
e->usage =
"Usage: ari set debug <application> <on|off>\n"
" Enable or disable debugging on a specific application.\n"
;
return NULL;
case CLI_GENERATE:
return complete_ari_show_app(a);
default:
break;
}
if (a->argc != 5) {
return CLI_SHOWUSAGE;
}
app = stasis_app_get_by_name(a->argv[3]);
if (!app) {
return CLI_FAILURE;
}
debug = !strcmp(a->argv[4], "on");
app_set_debug(app, debug);
ast_cli(a->fd, "Debugging on '%s' %s\n",
app_name(app),
debug ? "enabled" : "disabled");
ao2_ref(app, -1);
return CLI_SUCCESS;
}
static struct ast_cli_entry cli_ari[] = {
AST_CLI_DEFINE(ari_show_apps, "List registered ARI applications"),
AST_CLI_DEFINE(ari_show_app, "Display details of a registered ARI application"),
AST_CLI_DEFINE(ari_set_debug, "Enable/disable debugging of an ARI application"),
};
int cli_init(void)
{
return ast_cli_register_multiple(cli_ari, ARRAY_LEN(cli_ari));
}
void cli_cleanup(void)
{
ast_cli_unregister_multiple(cli_ari, ARRAY_LEN(cli_ari));
}

43
res/stasis/cli.h Normal file
View File

@ -0,0 +1,43 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2016, Digium, Inc.
*
* Matt Jordan <mjordan@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
#ifndef _ASTERISK_RES_STASIS_CLI_H
#define _ASTERISK_RES_STASIS_CLI_H
/*! \file
*
* \brief Internal API for Stasis application CLI commands
*
* \author Matt Jordan <mjordan@digium.com>
* \since 13.13.0
*/
/*!
* \brief Initialize the CLI commands
*
* \retval 0 on success
* \retval non-zero on error
*/
int cli_init(void);
/*!
* \brief Cleanup the CLI commands
*/
void cli_cleanup(void);
#endif /* _ASTERISK_RES_STASIS_CLI_H */