Merge "res/stasis: Add CLI commands for displaying/debugging ARI apps"
This commit is contained in:
commit
a2dde4a543
12
CHANGES
12
CHANGES
|
@ -69,6 +69,18 @@ chan_pjsip
|
|||
to asymmetric using the "asymmetric_rtp_codec" endpoint option. If this
|
||||
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 ----------
|
||||
------------------------------------------------------------------------------
|
||||
|
|
|
@ -76,6 +76,16 @@ typedef void (*stasis_app_cb)(void *data, const char *app_name,
|
|||
*/
|
||||
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.
|
||||
*
|
||||
|
|
|
@ -172,7 +172,9 @@ int ast_ari_websocket_session_write(struct ast_ari_websocket_session *session,
|
|||
return -1;
|
||||
}
|
||||
|
||||
#ifdef AST_DEVMODE
|
||||
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)) {
|
||||
ast_log(LOG_NOTICE, "Problem occurred during websocket write, websocket closed\n");
|
||||
return -1;
|
||||
|
|
|
@ -65,6 +65,7 @@
|
|||
#include "stasis/app.h"
|
||||
#include "stasis/control.h"
|
||||
#include "stasis/messaging.h"
|
||||
#include "stasis/cli.h"
|
||||
#include "stasis/stasis_bridge.h"
|
||||
#include "asterisk/core_unreal.h"
|
||||
#include "asterisk/musiconhold.h"
|
||||
|
@ -1487,6 +1488,11 @@ static struct stasis_app *find_app_by_name(const char *app_name)
|
|||
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)
|
||||
{
|
||||
struct stasis_app *app = obj;
|
||||
|
@ -1959,6 +1965,8 @@ static int unload_module(void)
|
|||
{
|
||||
stasis_app_unregister_event_sources();
|
||||
|
||||
cli_cleanup();
|
||||
|
||||
messaging_cleanup();
|
||||
|
||||
cleanup();
|
||||
|
@ -2118,6 +2126,11 @@ static int load_module(void)
|
|||
return AST_MODULE_LOAD_FAILURE;
|
||||
}
|
||||
|
||||
if (cli_init()) {
|
||||
unload_module();
|
||||
return AST_MODULE_LOAD_FAILURE;
|
||||
}
|
||||
|
||||
bridge_stasis_init();
|
||||
|
||||
stasis_app_register_event_sources();
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "messaging.h"
|
||||
|
||||
#include "asterisk/callerid.h"
|
||||
#include "asterisk/cli.h"
|
||||
#include "asterisk/stasis_app.h"
|
||||
#include "asterisk/stasis_bridges.h"
|
||||
#include "asterisk/stasis_channels.h"
|
||||
|
@ -59,6 +60,8 @@ struct stasis_app {
|
|||
void *data;
|
||||
/*! Subscription model for the application */
|
||||
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 */
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
stasis_app_cb handler;
|
||||
int debug;
|
||||
char eid[20];
|
||||
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 */
|
||||
{
|
||||
SCOPED_AO2LOCK(lock, app);
|
||||
debug = app->debug;
|
||||
handler = app->handler;
|
||||
if (app->data) {
|
||||
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 */
|
||||
}
|
||||
|
||||
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) {
|
||||
ast_verb(3,
|
||||
"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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
|
||||
|
|
|
@ -127,8 +127,26 @@ void app_send(struct stasis_app *app, struct ast_json *message);
|
|||
|
||||
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_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.
|
||||
*
|
||||
|
@ -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);
|
||||
|
||||
/*!
|
||||
* \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 */
|
||||
|
|
|
@ -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));
|
||||
}
|
|
@ -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 */
|
Loading…
Reference in New Issue