[Asterisk-code-review] ARI: Added new functionality to reload a single module. (asterisk[13])

Benjamin Keith Ford asteriskteam at digium.com
Thu Jul 9 14:03:39 CDT 2015


Benjamin Keith Ford has uploaded a new change for review.

  https://gerrit.asterisk.org/858

Change subject: ARI: Added new functionality to reload a single module.
......................................................................

ARI: Added new functionality to reload a single module.

An http request can be sent to reload an Asterisk module. If the
module can not be reloaded or is not already loaded, an error
response will be returned.

The command "curl -v -u user:pass -X PUT 'http://localhost:8088
/ari/asterisk/modules/{moduleName}'" (or something similar, based
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
* Asterisk modules can be reloaded through http requests

ASTERISK-25173

Change-Id: I289188bcae182b2083bdbd9ebfffd50b62f58ae1
---
M CHANGES
M include/asterisk/ari.h
M res/ari/ari_model_validators.c
M res/ari/ari_model_validators.h
M res/ari/resource_asterisk.c
M res/ari/resource_asterisk.h
M res/res_ari.c
M res/res_ari_asterisk.c
M rest-api/api-docs/asterisk.json
9 files changed, 363 insertions(+), 2 deletions(-)


  git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/58/858/1

diff --git a/CHANGES b/CHANGES
index 4b34fbe..cef6ca7 100644
--- a/CHANGES
+++ b/CHANGES
@@ -20,6 +20,11 @@
  * Added the Linkedid header to the common channel headers listed for each
    channel in AMI events.
 
+ARI
+------------------
+ * A new feature has been added that allows individual modules to be reloaded
+   to Asterisk 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/ari.h b/include/asterisk/ari.h
index 00769ee..c3df46a 100644
--- a/include/asterisk/ari.h
+++ b/include/asterisk/ari.h
@@ -225,6 +225,11 @@
 void ast_ari_response_no_content(struct ast_ari_response *response);
 
 /*!
+ * \brief Fill in a <tt>Accepted</tt> (202) \a ast_ari_response.
+ */
+void ast_ari_response_accepted(struct ast_ari_response *response);
+
+/*!
  * \brief Fill in a <tt>Created</tt> (201) \a ast_ari_response.
  * \param response Response to fill in.
  * \param url URL to the created resource.
diff --git a/res/ari/ari_model_validators.c b/res/ari/ari_model_validators.c
index fe61ad4..2f54b8d 100644
--- a/res/ari/ari_model_validators.c
+++ b/res/ari/ari_model_validators.c
@@ -308,6 +308,108 @@
 	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 1fafba3..41b9179 100644
--- a/res/ari/ari_model_validators.h
+++ b/res/ari/ari_model_validators.h
@@ -207,6 +207,24 @@
 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.
  *
  * Effective user/group id
@@ -1244,6 +1262,12 @@
  * - 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 9871df4..a9446f1 100644
--- a/res/ari/resource_asterisk.c
+++ b/res/ari/resource_asterisk.c
@@ -33,6 +33,7 @@
 
 #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,64 @@
 	ast_ari_response_ok(response, ast_json_ref(json));
 }
 
+void ast_ari_asterisk_reload_module(struct ast_variable *headers,
+	struct ast_ari_asterisk_reload_module_args *args,
+	struct ast_ari_response *response)
+{
+	enum ast_module_reload_result reload_result;
+
+	ast_assert(response != NULL);
+
+	if (ast_strlen_zero(args->module_name)) {
+		ast_ari_response_error(
+			response, 400, "Bad Request",
+			"Module name is required");
+		return;
+	}
+
+	if (!ast_module_check(args->module_name)) {
+		ast_verb(0, "made it here\n");
+		ast_ari_response_error(
+			response, 404, "Not Found",
+			"Module does not exist");
+		return;
+	}
+
+	reload_result = ast_module_reload(args->module_name);
+
+	if (reload_result == AST_MODULE_RELOAD_NOT_FOUND) {
+		ast_ari_response_error(
+			response, 404, "Not Found",
+			"Module could not be found");
+		return;
+	} else if (reload_result == AST_MODULE_RELOAD_ERROR) {
+		ast_ari_response_error(
+			response, 409, "Conflict",
+			"An error occurred while reloading the module");
+		return;
+	} else if (reload_result == AST_MODULE_RELOAD_IN_PROGRESS) {
+		ast_ari_response_error(
+			response, 409, "Conflict",
+			"Another reload is currently in progress");
+		return;
+	} else if (reload_result == AST_MODULE_RELOAD_UNINITIALIZED) {
+		ast_ari_response_error(
+			response, 409, "Conflict",
+			"Module has not been initialized");
+		return;
+	} else if (reload_result == AST_MODULE_RELOAD_NOT_IMPLEMENTED) {
+		ast_ari_response_error(
+			response, 409, "Conflict",
+			"Module does not support reloading");
+		return;
+	} else if (reload_result == AST_MODULE_RELOAD_QUEUED) {
+		ast_ari_response_accepted(response);
+		return;
+	}
+
+	ast_ari_response_no_content(response);
+}
+
 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 dc4b183..1635d28 100644
--- a/res/ari/resource_asterisk.h
+++ b/res/ari/resource_asterisk.h
@@ -67,6 +67,19 @@
  * \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_reload_module() */
+struct ast_ari_asterisk_reload_module_args {
+	/*! Module's name */
+	const char *module_name;
+};
+/*!
+ * \brief Reload an Asterisk module.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_asterisk_reload_module(struct ast_variable *headers, struct ast_ari_asterisk_reload_module_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.c b/res/res_ari.c
index 28f9be1..761c2dd 100644
--- a/res/res_ari.c
+++ b/res/res_ari.c
@@ -284,6 +284,13 @@
 	response->response_text = "No Content";
 }
 
+void ast_ari_response_accepted(struct ast_ari_response *response)
+{
+	response->message = ast_json_null();
+	response->response_code = 202;
+	response->response_text = "Accepted";
+}
+
 void ast_ari_response_alloc_failed(struct ast_ari_response *response)
 {
 	response->message = ast_json_ref(oom_json);
diff --git a/res/res_ari_asterisk.c b/res/res_ari_asterisk.c
index 6118297..31e357d 100644
--- a/res/res_ari_asterisk.c
+++ b/res/res_ari_asterisk.c
@@ -209,6 +209,67 @@
 	ast_free(args.only);
 	return;
 }
+/*!
+ * \brief Parameter parsing callback for /asterisk/modules/{moduleName}.
+ * \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_reload_module_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_reload_module_args args = {};
+	struct ast_variable *i;
+	RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
+#if defined(AST_DEVMODE)
+	int is_valid;
+	int code;
+#endif /* AST_DEVMODE */
+
+	for (i = path_vars; i; i = i->next) {
+		if (strcmp(i->name, "moduleName") == 0) {
+			args.module_name = (i->value);
+		} else
+		{}
+	}
+	ast_ari_asterisk_reload_module(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 */
+	case 400: /* Module name is required. */
+	case 404: /* Module does not exist. */
+	case 409: /* Module could not be reloaded. */
+		is_valid = 1;
+		break;
+	default:
+		if (200 <= code && code <= 299) {
+			is_valid = ast_ari_validate_void(
+				response->message);
+		} else {
+			ast_log(LOG_ERROR, "Invalid error response %d for /asterisk/modules/{moduleName}\n", code);
+			is_valid = 0;
+		}
+	}
+
+	if (!is_valid) {
+		ast_log(LOG_ERROR, "Response validation failed for /asterisk/modules/{moduleName}\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 +470,24 @@
 	.children = {  }
 };
 /*! \brief REST handler for /api-docs/asterisk.{format} */
+static struct stasis_rest_handlers asterisk_modules_moduleName = {
+	.path_segment = "moduleName",
+	.is_wildcard = 1,
+	.callbacks = {
+		[AST_HTTP_PUT] = ast_ari_asterisk_reload_module_cb,
+	},
+	.num_children = 0,
+	.children = {  }
+};
+/*! \brief REST handler for /api-docs/asterisk.{format} */
+static struct stasis_rest_handlers asterisk_modules = {
+	.path_segment = "modules",
+	.callbacks = {
+	},
+	.num_children = 1,
+	.children = { &asterisk_modules_moduleName, }
+};
+/*! \brief REST handler for /api-docs/asterisk.{format} */
 static struct stasis_rest_handlers asterisk_variable = {
 	.path_segment = "variable",
 	.callbacks = {
@@ -423,8 +502,8 @@
 	.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 263bfd6..8a67497 100644
--- a/rest-api/api-docs/asterisk.json
+++ b/rest-api/api-docs/asterisk.json
@@ -39,6 +39,42 @@
 			]
 		},
 		{
+			"path": "/asterisk/modules/{moduleName}",
+			"description": "Asterisk module",
+			"operations": [
+				{
+					"httpMethod": "PUT",
+					"summary": "Reload an Asterisk module.",
+					"nickname": "reloadModule",
+					"responseClass": "void",
+					"parameters": [
+						{
+							"name": "moduleName",
+							"description": "Module's name",
+							"paramType": "path",
+							"required": true,
+							"allowMultiple": false,
+							"dataType": "string"
+						}
+					],
+					"errorResponses": [
+						{
+							"code": 400,
+							"reason": "Module name is required."
+						},
+						{
+							"code": 404,
+							"reason": "Module does not exist."
+						},
+						{
+							"code": 409,
+							"reason": "Module could not be reloaded."
+						}
+					]
+				}
+			]
+		},
+		{
 			"path": "/asterisk/variable",
 			"description": "Global variables",
 			"operations": [
@@ -244,6 +280,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",

-- 
To view, visit https://gerrit.asterisk.org/858
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I289188bcae182b2083bdbd9ebfffd50b62f58ae1
Gerrit-PatchSet: 1
Gerrit-Project: asterisk
Gerrit-Branch: 13
Gerrit-Owner: Benjamin Keith Ford <bford at digium.com>



More information about the asterisk-code-review mailing list