Merge "res_sorcery_memory_cache: Add CLI commands and AMI actions."
This commit is contained in:
commit
3906175426
|
@ -38,6 +38,73 @@ ASTERISK_REGISTER_FILE()
|
|||
#include "asterisk/sched.h"
|
||||
#include "asterisk/test.h"
|
||||
#include "asterisk/heap.h"
|
||||
#include "asterisk/cli.h"
|
||||
#include "asterisk/manager.h"
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<manager name="SorceryMemoryCacheExpireObject" language="en_US">
|
||||
<synopsis>
|
||||
Expire (remove) an object from a sorcery memory cache.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
|
||||
<parameter name="Cache" required="true">
|
||||
<para>The name of the cache to expire the object from.</para>
|
||||
</parameter>
|
||||
<parameter name="Object" required="true">
|
||||
<para>The name of the object to expire.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Expires (removes) an object from a sorcery memory cache.</para>
|
||||
</description>
|
||||
</manager>
|
||||
<manager name="SorceryMemoryCacheExpire" language="en_US">
|
||||
<synopsis>
|
||||
Expire (remove) ALL objects from a sorcery memory cache.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
|
||||
<parameter name="Cache" required="true">
|
||||
<para>The name of the cache to expire all objects from.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Expires (removes) ALL objects from a sorcery memory cache.</para>
|
||||
</description>
|
||||
</manager>
|
||||
<manager name="SorceryMemoryCacheStaleObject" language="en_US">
|
||||
<synopsis>
|
||||
Mark an object in a sorcery memory cache as stale.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
|
||||
<parameter name="Cache" required="true">
|
||||
<para>The name of the cache to mark the object as stale in.</para>
|
||||
</parameter>
|
||||
<parameter name="Object" required="true">
|
||||
<para>The name of the object to mark as stale.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Marks an object as stale within a sorcery memory cache.</para>
|
||||
</description>
|
||||
</manager>
|
||||
<manager name="SorceryMemoryCacheStale" language="en_US">
|
||||
<synopsis>
|
||||
Marks ALL objects in a sorcery memory cache as stale.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
|
||||
<parameter name="Cache" required="true">
|
||||
<para>The name of the cache to mark all object as stale in.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Marks ALL objects in a sorcery memory cache as stale.</para>
|
||||
</description>
|
||||
</manager>
|
||||
***/
|
||||
|
||||
/*! \brief Structure for storing a memory cache */
|
||||
struct sorcery_memory_cache {
|
||||
|
@ -403,6 +470,94 @@ static int expire_objects_from_cache(const void *data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Remove all objects from the cache.
|
||||
*
|
||||
* This removes ALL objects from both the hash table and heap.
|
||||
*
|
||||
* \pre cache->objects is write-locked
|
||||
*
|
||||
* \param cache The cache to empty.
|
||||
*/
|
||||
static void remove_all_from_cache(struct sorcery_memory_cache *cache)
|
||||
{
|
||||
while (ast_heap_pop(cache->object_heap));
|
||||
|
||||
ao2_callback(cache->objects, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE,
|
||||
NULL, NULL);
|
||||
|
||||
AST_SCHED_DEL_UNREF(sched, cache->expire_id, ao2_ref(cache, -1));
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief AO2 callback function for making an object stale immediately
|
||||
*
|
||||
* This changes the creation time of an object so it appears as though it is stale immediately.
|
||||
*
|
||||
* \param obj The cached object
|
||||
* \param arg The cache itself
|
||||
* \param flags Unused flags
|
||||
*/
|
||||
static int object_stale_callback(void *obj, void *arg, int flags)
|
||||
{
|
||||
struct sorcery_memory_cached_object *cached = obj;
|
||||
struct sorcery_memory_cache *cache = arg;
|
||||
|
||||
/* Since our granularity is seconds it's possible for something to retrieve us within a window
|
||||
* where we wouldn't be treated as stale. To ensure that doesn't happen we use the configured stale
|
||||
* time plus a second.
|
||||
*/
|
||||
cached->created = ast_tvsub(cached->created, ast_samp2tv(cache->object_lifetime_stale + 1, 1));
|
||||
|
||||
return CMP_MATCH;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Mark an object as stale explicitly.
|
||||
*
|
||||
* This changes the creation time of an object so it appears as though it is stale immediately.
|
||||
*
|
||||
* \pre cache->objects is read-locked
|
||||
*
|
||||
* \param cache The cache the object is in
|
||||
* \param id The unique identifier of the object
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
*/
|
||||
static int mark_object_as_stale_in_cache(struct sorcery_memory_cache *cache, const char *id)
|
||||
{
|
||||
struct sorcery_memory_cached_object *cached;
|
||||
|
||||
cached = ao2_find(cache->objects, id, OBJ_SEARCH_KEY | OBJ_NOLOCK);
|
||||
if (!cached) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
object_stale_callback(cached, cache, 0);
|
||||
ao2_ref(cached, -1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Mark all objects as stale within a cache.
|
||||
*
|
||||
* This changes the creation time of ALL objects so they appear as though they are stale.
|
||||
*
|
||||
* \pre cache->objects is read-locked
|
||||
*
|
||||
* \param cache
|
||||
*/
|
||||
static void mark_all_as_stale_in_cache(struct sorcery_memory_cache *cache)
|
||||
{
|
||||
ao2_callback(cache->objects, OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE, object_stale_callback, cache);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Schedule a callback for cached object expiration.
|
||||
|
@ -900,6 +1055,480 @@ static void sorcery_memory_cache_close(void *data)
|
|||
ao2_ref(cache, -1);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief CLI tab completion for cache names
|
||||
*/
|
||||
static char *sorcery_memory_cache_complete_name(const char *word, int state)
|
||||
{
|
||||
struct sorcery_memory_cache *cache;
|
||||
struct ao2_iterator it_caches;
|
||||
int wordlen = strlen(word);
|
||||
int which = 0;
|
||||
char *result = NULL;
|
||||
|
||||
it_caches = ao2_iterator_init(caches, 0);
|
||||
while ((cache = ao2_iterator_next(&it_caches))) {
|
||||
if (!strncasecmp(word, cache->name, wordlen)
|
||||
&& ++which > state) {
|
||||
result = ast_strdup(cache->name);
|
||||
}
|
||||
ao2_ref(cache, -1);
|
||||
if (result) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ao2_iterator_destroy(&it_caches);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief CLI command implementation for 'sorcery memory cache show'
|
||||
*/
|
||||
static char *sorcery_memory_cache_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||
{
|
||||
struct sorcery_memory_cache *cache;
|
||||
|
||||
switch (cmd) {
|
||||
case CLI_INIT:
|
||||
e->command = "sorcery memory cache show";
|
||||
e->usage =
|
||||
"Usage: sorcery memory cache show <name>\n"
|
||||
" Show sorcery memory cache configuration and statistics.\n";
|
||||
return NULL;
|
||||
case CLI_GENERATE:
|
||||
if (a->pos == 4) {
|
||||
return sorcery_memory_cache_complete_name(a->word, a->n);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (a->argc != 5) {
|
||||
return CLI_SHOWUSAGE;
|
||||
}
|
||||
|
||||
cache = ao2_find(caches, a->argv[4], OBJ_SEARCH_KEY);
|
||||
if (!cache) {
|
||||
ast_cli(a->fd, "Specified sorcery memory cache '%s' does not exist\n", a->argv[4]);
|
||||
return CLI_FAILURE;
|
||||
}
|
||||
|
||||
ast_cli(a->fd, "Sorcery memory cache: %s\n", cache->name);
|
||||
ast_cli(a->fd, "Number of objects within cache: %d\n", ao2_container_count(cache->objects));
|
||||
if (cache->maximum_objects) {
|
||||
ast_cli(a->fd, "Maximum allowed objects: %d\n", cache->maximum_objects);
|
||||
} else {
|
||||
ast_cli(a->fd, "There is no limit on the maximum number of objects in the cache\n");
|
||||
}
|
||||
if (cache->object_lifetime_maximum) {
|
||||
ast_cli(a->fd, "Number of seconds before object expires: %d\n", cache->object_lifetime_maximum);
|
||||
} else {
|
||||
ast_cli(a->fd, "Object expiration is not enabled - cached objects will not expire\n");
|
||||
}
|
||||
if (cache->object_lifetime_stale) {
|
||||
ast_cli(a->fd, "Number of seconds before object becomes stale: %d\n", cache->object_lifetime_stale);
|
||||
} else {
|
||||
ast_cli(a->fd, "Object staleness is not enabled - cached objects will not go stale\n");
|
||||
}
|
||||
ast_cli(a->fd, "Prefetch: %s\n", AST_CLI_ONOFF(cache->prefetch));
|
||||
ast_cli(a->fd, "Expire all objects on reload: %s\n", AST_CLI_ONOFF(cache->expire_on_reload));
|
||||
|
||||
ao2_ref(cache, -1);
|
||||
|
||||
return CLI_SUCCESS;
|
||||
}
|
||||
|
||||
/*! \brief Structure used to pass data for printing cached object information */
|
||||
struct print_object_details {
|
||||
/*! \brief The sorcery memory cache */
|
||||
struct sorcery_memory_cache *cache;
|
||||
/*! \brief The CLI arguments */
|
||||
struct ast_cli_args *a;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Callback function for displaying object within the cache
|
||||
*/
|
||||
static int sorcery_memory_cache_print_object(void *obj, void *arg, int flags)
|
||||
{
|
||||
#define FORMAT "%-25.25s %-15u %-15u \n"
|
||||
struct sorcery_memory_cached_object *cached = obj;
|
||||
struct print_object_details *details = arg;
|
||||
int seconds_until_expire = 0, seconds_until_stale = 0;
|
||||
|
||||
if (details->cache->object_lifetime_maximum) {
|
||||
seconds_until_expire = ast_tvdiff_ms(ast_tvadd(cached->created, ast_samp2tv(details->cache->object_lifetime_maximum, 1)), ast_tvnow()) / 1000;
|
||||
}
|
||||
if (details->cache->object_lifetime_stale) {
|
||||
seconds_until_stale = ast_tvdiff_ms(ast_tvadd(cached->created, ast_samp2tv(details->cache->object_lifetime_stale, 1)), ast_tvnow()) / 1000;
|
||||
}
|
||||
|
||||
ast_cli(details->a->fd, FORMAT, ast_sorcery_object_get_id(cached->object), MAX(seconds_until_stale, 0), MAX(seconds_until_expire, 0));
|
||||
|
||||
return CMP_MATCH;
|
||||
#undef FORMAT
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief CLI command implementation for 'sorcery memory cache dump'
|
||||
*/
|
||||
static char *sorcery_memory_cache_dump(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||
{
|
||||
#define FORMAT "%-25.25s %-15.15s %-15.15s \n"
|
||||
struct sorcery_memory_cache *cache;
|
||||
struct print_object_details details;
|
||||
|
||||
switch (cmd) {
|
||||
case CLI_INIT:
|
||||
e->command = "sorcery memory cache dump";
|
||||
e->usage =
|
||||
"Usage: sorcery memory cache dump <name>\n"
|
||||
" Dump a list of the objects within the cache, listed by object identifier.\n";
|
||||
return NULL;
|
||||
case CLI_GENERATE:
|
||||
if (a->pos == 4) {
|
||||
return sorcery_memory_cache_complete_name(a->word, a->n);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (a->argc != 5) {
|
||||
return CLI_SHOWUSAGE;
|
||||
}
|
||||
|
||||
cache = ao2_find(caches, a->argv[4], OBJ_SEARCH_KEY);
|
||||
if (!cache) {
|
||||
ast_cli(a->fd, "Specified sorcery memory cache '%s' does not exist\n", a->argv[4]);
|
||||
return CLI_FAILURE;
|
||||
}
|
||||
|
||||
details.cache = cache;
|
||||
details.a = a;
|
||||
|
||||
ast_cli(a->fd, "Dumping sorcery memory cache '%s':\n", cache->name);
|
||||
if (!cache->object_lifetime_stale) {
|
||||
ast_cli(a->fd, " * Staleness is not enabled - objects will not go stale\n");
|
||||
}
|
||||
if (!cache->object_lifetime_maximum) {
|
||||
ast_cli(a->fd, " * Object lifetime is not enabled - objects will not expire\n");
|
||||
}
|
||||
ast_cli(a->fd, FORMAT, "Object Name", "Stale In", "Expires In");
|
||||
ast_cli(a->fd, FORMAT, "-------------------------", "---------------", "---------------");
|
||||
ao2_callback(cache->objects, OBJ_NODATA | OBJ_MULTIPLE, sorcery_memory_cache_print_object, &details);
|
||||
ast_cli(a->fd, FORMAT, "-------------------------", "---------------", "---------------");
|
||||
ast_cli(a->fd, "Total number of objects cached: %d\n", ao2_container_count(cache->objects));
|
||||
|
||||
ao2_ref(cache, -1);
|
||||
|
||||
return CLI_SUCCESS;
|
||||
#undef FORMAT
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief CLI tab completion for cached object names
|
||||
*/
|
||||
static char *sorcery_memory_cache_complete_object_name(const char *cache_name, const char *word, int state)
|
||||
{
|
||||
struct sorcery_memory_cache *cache;
|
||||
struct sorcery_memory_cached_object *cached;
|
||||
struct ao2_iterator it_cached;
|
||||
int wordlen = strlen(word);
|
||||
int which = 0;
|
||||
char *result = NULL;
|
||||
|
||||
cache = ao2_find(caches, cache_name, OBJ_SEARCH_KEY);
|
||||
if (!cache) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
it_cached = ao2_iterator_init(cache->objects, 0);
|
||||
while ((cached = ao2_iterator_next(&it_cached))) {
|
||||
if (!strncasecmp(word, ast_sorcery_object_get_id(cached->object), wordlen)
|
||||
&& ++which > state) {
|
||||
result = ast_strdup(ast_sorcery_object_get_id(cached->object));
|
||||
}
|
||||
ao2_ref(cached, -1);
|
||||
if (result) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ao2_iterator_destroy(&it_cached);
|
||||
|
||||
ao2_ref(cache, -1);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief CLI command implementation for 'sorcery memory cache expire'
|
||||
*/
|
||||
static char *sorcery_memory_cache_expire(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||
{
|
||||
struct sorcery_memory_cache *cache;
|
||||
|
||||
switch (cmd) {
|
||||
case CLI_INIT:
|
||||
e->command = "sorcery memory cache expire";
|
||||
e->usage =
|
||||
"Usage: sorcery memory cache expire <cache name> [object name]\n"
|
||||
" Expire a specific object or ALL objects within a sorcery memory cache.\n";
|
||||
return NULL;
|
||||
case CLI_GENERATE:
|
||||
if (a->pos == 4) {
|
||||
return sorcery_memory_cache_complete_name(a->word, a->n);
|
||||
} else if (a->pos == 5) {
|
||||
return sorcery_memory_cache_complete_object_name(a->argv[4], a->word, a->n);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (a->argc > 6) {
|
||||
return CLI_SHOWUSAGE;
|
||||
}
|
||||
|
||||
cache = ao2_find(caches, a->argv[4], OBJ_SEARCH_KEY);
|
||||
if (!cache) {
|
||||
ast_cli(a->fd, "Specified sorcery memory cache '%s' does not exist\n", a->argv[4]);
|
||||
return CLI_FAILURE;
|
||||
}
|
||||
|
||||
ao2_wrlock(cache->objects);
|
||||
if (a->argc == 5) {
|
||||
remove_all_from_cache(cache);
|
||||
ast_cli(a->fd, "All objects have been removed from cache '%s'\n", a->argv[4]);
|
||||
} else {
|
||||
if (!remove_from_cache(cache, a->argv[5], 1)) {
|
||||
ast_cli(a->fd, "Successfully expired object '%s' from cache '%s'\n", a->argv[5], a->argv[4]);
|
||||
} else {
|
||||
ast_cli(a->fd, "Object '%s' was not expired from cache '%s' as it was not found\n", a->argv[5],
|
||||
a->argv[4]);
|
||||
}
|
||||
}
|
||||
ao2_unlock(cache->objects);
|
||||
|
||||
ao2_ref(cache, -1);
|
||||
|
||||
return CLI_SUCCESS;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief CLI command implementation for 'sorcery memory cache stale'
|
||||
*/
|
||||
static char *sorcery_memory_cache_stale(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||
{
|
||||
struct sorcery_memory_cache *cache;
|
||||
|
||||
switch (cmd) {
|
||||
case CLI_INIT:
|
||||
e->command = "sorcery memory cache stale";
|
||||
e->usage =
|
||||
"Usage: sorcery memory cache stale <cache name> [object name]\n"
|
||||
" Mark a specific object or ALL objects as stale in a sorcery memory cache.\n";
|
||||
return NULL;
|
||||
case CLI_GENERATE:
|
||||
if (a->pos == 4) {
|
||||
return sorcery_memory_cache_complete_name(a->word, a->n);
|
||||
} else if (a->pos == 5) {
|
||||
return sorcery_memory_cache_complete_object_name(a->argv[4], a->word, a->n);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (a->argc > 6) {
|
||||
return CLI_SHOWUSAGE;
|
||||
}
|
||||
|
||||
cache = ao2_find(caches, a->argv[4], OBJ_SEARCH_KEY);
|
||||
if (!cache) {
|
||||
ast_cli(a->fd, "Specified sorcery memory cache '%s' does not exist\n", a->argv[4]);
|
||||
return CLI_FAILURE;
|
||||
}
|
||||
|
||||
if (!cache->object_lifetime_stale) {
|
||||
ast_cli(a->fd, "Specified sorcery memory cache '%s' does not have staleness enabled\n", a->argv[4]);
|
||||
ao2_ref(cache, -1);
|
||||
return CLI_FAILURE;
|
||||
}
|
||||
|
||||
ao2_rdlock(cache->objects);
|
||||
if (a->argc == 5) {
|
||||
mark_all_as_stale_in_cache(cache);
|
||||
ast_cli(a->fd, "Marked all objects in sorcery memory cache '%s' as stale\n", a->argv[4]);
|
||||
} else {
|
||||
if (!mark_object_as_stale_in_cache(cache, a->argv[5])) {
|
||||
ast_cli(a->fd, "Successfully marked object '%s' in memory cache '%s' as stale\n",
|
||||
a->argv[5], a->argv[4]);
|
||||
} else {
|
||||
ast_cli(a->fd, "Object '%s' in sorcery memory cache '%s' could not be marked as stale as it was not found\n",
|
||||
a->argv[5], a->argv[4]);
|
||||
}
|
||||
}
|
||||
ao2_unlock(cache->objects);
|
||||
|
||||
ao2_ref(cache, -1);
|
||||
|
||||
return CLI_SUCCESS;
|
||||
}
|
||||
|
||||
static struct ast_cli_entry cli_memory_cache[] = {
|
||||
AST_CLI_DEFINE(sorcery_memory_cache_show, "Show sorcery memory cache information"),
|
||||
AST_CLI_DEFINE(sorcery_memory_cache_dump, "Dump all objects within a sorcery memory cache"),
|
||||
AST_CLI_DEFINE(sorcery_memory_cache_expire, "Expire a specific object or ALL objects within a sorcery memory cache"),
|
||||
AST_CLI_DEFINE(sorcery_memory_cache_stale, "Mark a specific object or ALL objects as stale within a sorcery memory cache"),
|
||||
};
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief AMI command implementation for 'SorceryMemoryCacheExpireObject'
|
||||
*/
|
||||
static int sorcery_memory_cache_ami_expire_object(struct mansession *s, const struct message *m)
|
||||
{
|
||||
const char *cache_name = astman_get_header(m, "Cache");
|
||||
const char *object_name = astman_get_header(m, "Object");
|
||||
struct sorcery_memory_cache *cache;
|
||||
int res;
|
||||
|
||||
if (ast_strlen_zero(cache_name)) {
|
||||
astman_send_error(s, m, "SorceryMemoryCacheExpireObject requires that a cache name be provided.\n");
|
||||
return 0;
|
||||
} else if (ast_strlen_zero(object_name)) {
|
||||
astman_send_error(s, m, "SorceryMemoryCacheExpireObject requires that an object name be provided\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
cache = ao2_find(caches, cache_name, OBJ_SEARCH_KEY);
|
||||
if (!cache) {
|
||||
astman_send_error(s, m, "The provided cache does not exist\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ao2_wrlock(cache->objects);
|
||||
res = remove_from_cache(cache, object_name, 1);
|
||||
ao2_unlock(cache->objects);
|
||||
|
||||
ao2_ref(cache, -1);
|
||||
|
||||
if (!res) {
|
||||
astman_send_ack(s, m, "The provided object was expired from the cache\n");
|
||||
} else {
|
||||
astman_send_error(s, m, "The provided object could not be expired from the cache\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief AMI command implementation for 'SorceryMemoryCacheExpire'
|
||||
*/
|
||||
static int sorcery_memory_cache_ami_expire(struct mansession *s, const struct message *m)
|
||||
{
|
||||
const char *cache_name = astman_get_header(m, "Cache");
|
||||
struct sorcery_memory_cache *cache;
|
||||
|
||||
if (ast_strlen_zero(cache_name)) {
|
||||
astman_send_error(s, m, "SorceryMemoryCacheExpire requires that a cache name be provided.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
cache = ao2_find(caches, cache_name, OBJ_SEARCH_KEY);
|
||||
if (!cache) {
|
||||
astman_send_error(s, m, "The provided cache does not exist\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ao2_wrlock(cache->objects);
|
||||
remove_all_from_cache(cache);
|
||||
ao2_unlock(cache->objects);
|
||||
|
||||
ao2_ref(cache, -1);
|
||||
|
||||
astman_send_ack(s, m, "All objects were expired from the cache\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief AMI command implementation for 'SorceryMemoryCacheStaleObject'
|
||||
*/
|
||||
static int sorcery_memory_cache_ami_stale_object(struct mansession *s, const struct message *m)
|
||||
{
|
||||
const char *cache_name = astman_get_header(m, "Cache");
|
||||
const char *object_name = astman_get_header(m, "Object");
|
||||
struct sorcery_memory_cache *cache;
|
||||
int res;
|
||||
|
||||
if (ast_strlen_zero(cache_name)) {
|
||||
astman_send_error(s, m, "SorceryMemoryCacheStaleObject requires that a cache name be provided.\n");
|
||||
return 0;
|
||||
} else if (ast_strlen_zero(object_name)) {
|
||||
astman_send_error(s, m, "SorceryMemoryCacheStaleObject requires that an object name be provided\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
cache = ao2_find(caches, cache_name, OBJ_SEARCH_KEY);
|
||||
if (!cache) {
|
||||
astman_send_error(s, m, "The provided cache does not exist\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ao2_rdlock(cache->objects);
|
||||
res = mark_object_as_stale_in_cache(cache, object_name);
|
||||
ao2_unlock(cache->objects);
|
||||
|
||||
ao2_ref(cache, -1);
|
||||
|
||||
if (!res) {
|
||||
astman_send_ack(s, m, "The provided object was marked as stale in the cache\n");
|
||||
} else {
|
||||
astman_send_error(s, m, "The provided object could not be marked as stale in the cache\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief AMI command implementation for 'SorceryMemoryCacheStale'
|
||||
*/
|
||||
static int sorcery_memory_cache_ami_stale(struct mansession *s, const struct message *m)
|
||||
{
|
||||
const char *cache_name = astman_get_header(m, "Cache");
|
||||
struct sorcery_memory_cache *cache;
|
||||
|
||||
if (ast_strlen_zero(cache_name)) {
|
||||
astman_send_error(s, m, "SorceryMemoryCacheStale requires that a cache name be provided.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
cache = ao2_find(caches, cache_name, OBJ_SEARCH_KEY);
|
||||
if (!cache) {
|
||||
astman_send_error(s, m, "The provided cache does not exist\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ao2_rdlock(cache->objects);
|
||||
mark_all_as_stale_in_cache(cache);
|
||||
ao2_unlock(cache->objects);
|
||||
|
||||
ao2_ref(cache, -1);
|
||||
|
||||
astman_send_ack(s, m, "All objects were marked as stale in the cache\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef TEST_FRAMEWORK
|
||||
|
||||
/*! \brief Dummy sorcery object */
|
||||
|
@ -1846,6 +2475,13 @@ static int unload_module(void)
|
|||
|
||||
ast_sorcery_wizard_unregister(&memory_cache_object_wizard);
|
||||
|
||||
ast_cli_unregister_multiple(cli_memory_cache, ARRAY_LEN(cli_memory_cache));
|
||||
|
||||
ast_manager_unregister("SorceryMemoryCacheExpireObject");
|
||||
ast_manager_unregister("SorceryMemoryCacheExpire");
|
||||
ast_manager_unregister("SorceryMemoryCacheStaleObject");
|
||||
ast_manager_unregister("SorceryMemoryCacheStale");
|
||||
|
||||
AST_TEST_UNREGISTER(open_with_valid_options);
|
||||
AST_TEST_UNREGISTER(open_with_invalid_options);
|
||||
AST_TEST_UNREGISTER(create_and_retrieve);
|
||||
|
@ -1860,6 +2496,8 @@ static int unload_module(void)
|
|||
|
||||
static int load_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
sched = ast_sched_context_create();
|
||||
if (!sched) {
|
||||
ast_log(LOG_ERROR, "Failed to create scheduler for cache management\n");
|
||||
|
@ -1886,6 +2524,17 @@ static int load_module(void)
|
|||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
||||
res = ast_cli_register_multiple(cli_memory_cache, ARRAY_LEN(cli_memory_cache));
|
||||
res |= ast_manager_register_xml("SorceryMemoryCacheExpireObject", EVENT_FLAG_SYSTEM, sorcery_memory_cache_ami_expire_object);
|
||||
res |= ast_manager_register_xml("SorceryMemoryCacheExpire", EVENT_FLAG_SYSTEM, sorcery_memory_cache_ami_expire);
|
||||
res |= ast_manager_register_xml("SorceryMemoryCacheStaleObject", EVENT_FLAG_SYSTEM, sorcery_memory_cache_ami_stale_object);
|
||||
res |= ast_manager_register_xml("SorceryMemoryCacheStale", EVENT_FLAG_SYSTEM, sorcery_memory_cache_ami_stale);
|
||||
|
||||
if (res) {
|
||||
unload_module();
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
||||
AST_TEST_REGISTER(open_with_valid_options);
|
||||
AST_TEST_REGISTER(open_with_invalid_options);
|
||||
AST_TEST_REGISTER(create_and_retrieve);
|
||||
|
|
Loading…
Reference in New Issue