From 47ea312b2402bf31a0f1ccb9d73a6b8b7d814b09 Mon Sep 17 00:00:00 2001 From: Benjamin Ford Date: Fri, 26 Jun 2015 10:57:15 -0500 Subject: ARI: Added new functionality to get all module information. An http request can be sent to retrieve a list of all existing modules, including the resource name, description, use count, status, and support level. The command "curl -v -u user:pass -X GET 'http://localhost:8088/ari/ asterisk/modules" (or something similar, depending on configuration) can be run in the terminal to access this new functionality. For more information, see: https://wiki.asterisk.org/wiki.display/~bford/Asterisk+ARI+Resource * Added new ARI functionality * Information on modules can now be retrieved Change-Id: I63cbbf0ec0c3544cc45ed2a588dceabe91c5e0b0 --- CHANGES | 5 ++ include/asterisk/module.h | 18 +++++++ main/loader.c | 26 ++++++++++ res/ari/ari_model_validators.c | 102 ++++++++++++++++++++++++++++++++++++++++ res/ari/ari_model_validators.h | 24 ++++++++++ res/ari/resource_asterisk.c | 45 ++++++++++++++++++ res/ari/resource_asterisk.h | 11 +++++ res/res_ari_asterisk.c | 64 ++++++++++++++++++++++++- rest-api/api-docs/asterisk.json | 43 +++++++++++++++++ 9 files changed, 336 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 4b34fbe11..d4403c44c 100644 --- a/CHANGES +++ b/CHANGES @@ -20,6 +20,11 @@ AMI * Added the Linkedid header to the common channel headers listed for each channel in AMI events. +ARI +------------------ + * A new feature has been added that enables the retrieval of modules and + module information through an HTTP request. + res_pjsip ------------------ * A new 'g726_non_standard' endpoint option has been added that, when set to diff --git a/include/asterisk/module.h b/include/asterisk/module.h index 6d5e04dfc..0d1eb4eb6 100644 --- a/include/asterisk/module.h +++ b/include/asterisk/module.h @@ -160,6 +160,24 @@ int ast_update_module_list(int (*modentry)(const char *module, const char *descr enum ast_module_support_level support_level), const char *like); +/*! + * \brief Ask for a list of modules, descriptions, use counts and status. + * \param modentry A callback to an updater function + * \param like + * \param data Data passed into the callback for manipulation + * + * For each of the modules loaded, modentry will be executed with the resource, + * description, and usecount values of each particular module. + * + * \return the number of modules loaded + * \since 13.5.0 + */ +int ast_update_module_list_data(int (*modentry)(const char *module, const char *description, + int usecnt, const char *status, const char *like, + enum ast_module_support_level support_level, + void *data), + const char *like, void *data); + /*! * \brief Check if module with the name given is loaded * \param name Module name, like "chan_sip.so" diff --git a/main/loader.c b/main/loader.c index 814bb9768..55fed7063 100644 --- a/main/loader.c +++ b/main/loader.c @@ -1425,6 +1425,32 @@ int ast_update_module_list(int (*modentry)(const char *module, const char *descr return total_mod_loaded; } +int ast_update_module_list_data(int (*modentry)(const char *module, const char *description, + int usecnt, const char *status, const char *like, + enum ast_module_support_level support_level, + void *data), + const char *like, void *data) +{ + struct ast_module *cur; + int total_mod_loaded = 0; + AST_LIST_HEAD_NOLOCK(, ast_module) alpha_module_list = AST_LIST_HEAD_NOLOCK_INIT_VALUE; + + AST_DLLIST_LOCK(&module_list); + + AST_DLLIST_TRAVERSE(&module_list, cur, entry) { + AST_LIST_INSERT_SORTALPHA(&alpha_module_list, cur, list_entry, resource); + } + + while ((cur = AST_LIST_REMOVE_HEAD(&alpha_module_list, list_entry))) { + total_mod_loaded += modentry(cur->resource, cur->info->description, cur->usecount, + cur->flags.running? "Running" : "Not Running", like, cur->info->support_level, data); + } + + AST_DLLIST_UNLOCK(&module_list); + + return total_mod_loaded; +} + /*! \brief Check if module exists */ int ast_module_check(const char *name) { diff --git a/res/ari/ari_model_validators.c b/res/ari/ari_model_validators.c index fe61ad4a2..2f54b8d17 100644 --- a/res/ari/ari_model_validators.c +++ b/res/ari/ari_model_validators.c @@ -308,6 +308,108 @@ ari_validator ast_ari_validate_config_info_fn(void) return ast_ari_validate_config_info; } +int ast_ari_validate_module(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_description = 0; + int has_name = 0; + int has_status = 0; + int has_support_level = 0; + int has_use_count = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("description", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_description = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Module field description failed validation\n"); + res = 0; + } + } else + if (strcmp("name", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_name = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Module field name failed validation\n"); + res = 0; + } + } else + if (strcmp("status", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_status = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Module field status failed validation\n"); + res = 0; + } + } else + if (strcmp("support_level", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_support_level = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Module field support_level failed validation\n"); + res = 0; + } + } else + if (strcmp("use_count", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_use_count = 1; + prop_is_valid = ast_ari_validate_int( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Module field use_count failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI Module has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_description) { + ast_log(LOG_ERROR, "ARI Module missing required field description\n"); + res = 0; + } + + if (!has_name) { + ast_log(LOG_ERROR, "ARI Module missing required field name\n"); + res = 0; + } + + if (!has_status) { + ast_log(LOG_ERROR, "ARI Module missing required field status\n"); + res = 0; + } + + if (!has_support_level) { + ast_log(LOG_ERROR, "ARI Module missing required field support_level\n"); + res = 0; + } + + if (!has_use_count) { + ast_log(LOG_ERROR, "ARI Module missing required field use_count\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_module_fn(void) +{ + return ast_ari_validate_module; +} + int ast_ari_validate_set_id(struct ast_json *json) { int res = 1; diff --git a/res/ari/ari_model_validators.h b/res/ari/ari_model_validators.h index 1fafba3b5..41b91791d 100644 --- a/res/ari/ari_model_validators.h +++ b/res/ari/ari_model_validators.h @@ -206,6 +206,24 @@ int ast_ari_validate_config_info(struct ast_json *json); */ ari_validator ast_ari_validate_config_info_fn(void); +/*! + * \brief Validator for Module. + * + * Details of an Asterisk module + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_module(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_module(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_module_fn(void); + /*! * \brief Validator for SetId. * @@ -1244,6 +1262,12 @@ ari_validator ast_ari_validate_application_fn(void); * - max_open_files: int * - name: string (required) * - setid: SetId (required) + * Module + * - description: string (required) + * - name: string (required) + * - status: string (required) + * - support_level: string (required) + * - use_count: int (required) * SetId * - group: string (required) * - user: string (required) diff --git a/res/ari/resource_asterisk.c b/res/ari/resource_asterisk.c index 9871df4ff..c81bdb384 100644 --- a/res/ari/resource_asterisk.c +++ b/res/ari/resource_asterisk.c @@ -33,6 +33,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/ast_version.h" #include "asterisk/buildinfo.h" +#include "asterisk/module.h" #include "asterisk/paths.h" #include "asterisk/pbx.h" #include "resource_asterisk.h" @@ -140,6 +141,50 @@ void ast_ari_asterisk_get_info(struct ast_variable *headers, ast_ari_response_ok(response, ast_json_ref(json)); } +/*! + * \brief Process module information and append to a json array + * \param module Resource name + * \param description Resource description + * \param usecnt Resource use count + * \param status Resource running status + * \param like + * \param support_level Resource support level + * \param module_data_list Resource array + * + * \retval 0 if no resource exists + * \retval 1 if resource exists + */ +static int process_module_list(const char *module, const char *description, int usecnt, + const char *status, const char *like, + enum ast_module_support_level support_level, void *module_data_list) +{ + struct ast_json *module_info; + + module_info = ast_json_pack("{s: s, s: s, s: i, s: s, s: s}", + "name", module, + "description", description, + "use_count", usecnt, + "status", status, + "support_level", ast_module_support_level_to_string(support_level)); + if (!module_info) { + return 0; + } + ast_json_array_append(module_data_list, module_info); + return 1; +} + +void ast_ari_asterisk_list_modules(struct ast_variable *headers, + struct ast_ari_asterisk_list_modules_args *args, + struct ast_ari_response *response) +{ + struct ast_json *json; + + json = ast_json_array_create(); + ast_update_module_list_data(&process_module_list, NULL, json); + + ast_ari_response_ok(response, json); +} + void ast_ari_asterisk_get_global_var(struct ast_variable *headers, struct ast_ari_asterisk_get_global_var_args *args, struct ast_ari_response *response) diff --git a/res/ari/resource_asterisk.h b/res/ari/resource_asterisk.h index dc4b183f7..b09d2715a 100644 --- a/res/ari/resource_asterisk.h +++ b/res/ari/resource_asterisk.h @@ -67,6 +67,17 @@ int ast_ari_asterisk_get_info_parse_body( * \param[out] response HTTP response */ void ast_ari_asterisk_get_info(struct ast_variable *headers, struct ast_ari_asterisk_get_info_args *args, struct ast_ari_response *response); +/*! Argument struct for ast_ari_asterisk_list_modules() */ +struct ast_ari_asterisk_list_modules_args { +}; +/*! + * \brief List Asterisk modules. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_asterisk_list_modules(struct ast_variable *headers, struct ast_ari_asterisk_list_modules_args *args, struct ast_ari_response *response); /*! Argument struct for ast_ari_asterisk_get_global_var() */ struct ast_ari_asterisk_get_global_var_args { /*! The variable to get */ diff --git a/res/res_ari_asterisk.c b/res/res_ari_asterisk.c index 611829768..85bf78e06 100644 --- a/res/res_ari_asterisk.c +++ b/res/res_ari_asterisk.c @@ -209,6 +209,57 @@ fin: __attribute__((unused)) ast_free(args.only); return; } +/*! + * \brief Parameter parsing callback for /asterisk/modules. + * \param get_params GET parameters in the HTTP request. + * \param path_vars Path variables extracted from the request. + * \param headers HTTP headers. + * \param[out] response Response to the HTTP request. + */ +static void ast_ari_asterisk_list_modules_cb( + struct ast_tcptls_session_instance *ser, + struct ast_variable *get_params, struct ast_variable *path_vars, + struct ast_variable *headers, struct ast_ari_response *response) +{ + struct ast_ari_asterisk_list_modules_args args = {}; + RAII_VAR(struct ast_json *, body, NULL, ast_json_unref); +#if defined(AST_DEVMODE) + int is_valid; + int code; +#endif /* AST_DEVMODE */ + + ast_ari_asterisk_list_modules(headers, &args, response); +#if defined(AST_DEVMODE) + code = response->response_code; + + switch (code) { + case 0: /* Implementation is still a stub, or the code wasn't set */ + is_valid = response->message == NULL; + break; + case 500: /* Internal Server Error */ + case 501: /* Not Implemented */ + is_valid = 1; + break; + default: + if (200 <= code && code <= 299) { + is_valid = ast_ari_validate_list(response->message, + ast_ari_validate_module_fn()); + } else { + ast_log(LOG_ERROR, "Invalid error response %d for /asterisk/modules\n", code); + is_valid = 0; + } + } + + if (!is_valid) { + ast_log(LOG_ERROR, "Response validation failed for /asterisk/modules\n"); + ast_ari_response_error(response, 500, + "Internal Server Error", "Response validation failed"); + } +#endif /* AST_DEVMODE */ + +fin: __attribute__((unused)) + return; +} int ast_ari_asterisk_get_global_var_parse_body( struct ast_json *body, struct ast_ari_asterisk_get_global_var_args *args) @@ -409,6 +460,15 @@ static struct stasis_rest_handlers asterisk_info = { .children = { } }; /*! \brief REST handler for /api-docs/asterisk.{format} */ +static struct stasis_rest_handlers asterisk_modules = { + .path_segment = "modules", + .callbacks = { + [AST_HTTP_GET] = ast_ari_asterisk_list_modules_cb, + }, + .num_children = 0, + .children = { } +}; +/*! \brief REST handler for /api-docs/asterisk.{format} */ static struct stasis_rest_handlers asterisk_variable = { .path_segment = "variable", .callbacks = { @@ -423,8 +483,8 @@ static struct stasis_rest_handlers asterisk = { .path_segment = "asterisk", .callbacks = { }, - .num_children = 2, - .children = { &asterisk_info,&asterisk_variable, } + .num_children = 3, + .children = { &asterisk_info,&asterisk_modules,&asterisk_variable, } }; static int load_module(void) diff --git a/rest-api/api-docs/asterisk.json b/rest-api/api-docs/asterisk.json index 263bfd614..ade5938c9 100644 --- a/rest-api/api-docs/asterisk.json +++ b/rest-api/api-docs/asterisk.json @@ -38,6 +38,18 @@ } ] }, + { + "path": "/asterisk/modules", + "description": "Asterisk modules", + "operations": [ + { + "httpMethod": "GET", + "summary": "List Asterisk modules.", + "nickname": "listModules", + "responseClass": "List[Module]" + } + ] + }, { "path": "/asterisk/variable", "description": "Global variables", @@ -244,6 +256,37 @@ } } }, + "Module": { + "id": "Module", + "description": "Details of an Asterisk module", + "properties": { + "name": { + "type": "string", + "description": "The name of this module", + "required": true + }, + "description": { + "type": "string", + "description": "The description of this module", + "required": true + }, + "use_count": { + "type": "int", + "description": "The number of times this module is being used", + "required": true + }, + "status": { + "type": "string", + "description": "The running status of this module", + "required": true + }, + "support_level": { + "type": "string", + "description": "The support state of this module", + "required": true + } + } + }, "Variable": { "id": "Variable", "description": "The value of a channel variable", -- cgit v1.2.3