Fix to MXS-438: https://mariadb.atlassian.net/browse/MXS-438
Maxinfo now supports the shutdown command which shuts down a service, monitor or MaxScale itself and the restart command which restarts a stopped monitor or service.
This commit is contained in:
@ -82,6 +82,7 @@ SERVER *server;
|
|||||||
server->rlag = -2;
|
server->rlag = -2;
|
||||||
server->master_id = -1;
|
server->master_id = -1;
|
||||||
server->depth = -1;
|
server->depth = -1;
|
||||||
|
spinlock_init(&server->lock);
|
||||||
server->persistent = NULL;
|
server->persistent = NULL;
|
||||||
server->persistmax = 0;
|
server->persistmax = 0;
|
||||||
spinlock_init(&server->persistlock);
|
spinlock_init(&server->persistlock);
|
||||||
@ -663,6 +664,7 @@ char *status = NULL;
|
|||||||
void
|
void
|
||||||
server_set_status(SERVER *server, int bit)
|
server_set_status(SERVER *server, int bit)
|
||||||
{
|
{
|
||||||
|
spinlock_acquire(&server->lock);
|
||||||
server->status |= bit;
|
server->status |= bit;
|
||||||
|
|
||||||
/** clear error logged flag before the next failure */
|
/** clear error logged flag before the next failure */
|
||||||
@ -670,6 +672,7 @@ server_set_status(SERVER *server, int bit)
|
|||||||
{
|
{
|
||||||
server->master_err_is_logged = false;
|
server->master_err_is_logged = false;
|
||||||
}
|
}
|
||||||
|
spinlock_release(&server->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -681,7 +684,9 @@ server_set_status(SERVER *server, int bit)
|
|||||||
void
|
void
|
||||||
server_clear_status(SERVER *server, int bit)
|
server_clear_status(SERVER *server, int bit)
|
||||||
{
|
{
|
||||||
|
spinlock_acquire(&server->lock);
|
||||||
server->status &= ~bit;
|
server->status &= ~bit;
|
||||||
|
spinlock_release(&server->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -81,6 +81,7 @@ typedef struct server {
|
|||||||
#if defined(SS_DEBUG)
|
#if defined(SS_DEBUG)
|
||||||
skygw_chk_t server_chk_top;
|
skygw_chk_t server_chk_top;
|
||||||
#endif
|
#endif
|
||||||
|
SPINLOCK lock; /**< Common access lock */
|
||||||
char *unique_name; /**< Unique name for the server */
|
char *unique_name; /**< Unique name for the server */
|
||||||
char *name; /**< Server name/IP address*/
|
char *name; /**< Server name/IP address*/
|
||||||
unsigned short port; /**< Port to listen on */
|
unsigned short port; /**< Port to listen on */
|
||||||
|
@ -76,7 +76,9 @@ typedef enum
|
|||||||
MAXOP_EQUAL,
|
MAXOP_EQUAL,
|
||||||
MAXOP_FLUSH,
|
MAXOP_FLUSH,
|
||||||
MAXOP_SET,
|
MAXOP_SET,
|
||||||
MAXOP_CLEAR
|
MAXOP_CLEAR,
|
||||||
|
MAXOP_SHUTDOWN,
|
||||||
|
MAXOP_RESTART
|
||||||
} MAXINFO_OPERATOR;
|
} MAXINFO_OPERATOR;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -115,6 +117,8 @@ typedef struct maxinfo_tree {
|
|||||||
#define LT_FLUSH 10
|
#define LT_FLUSH 10
|
||||||
#define LT_SET 11
|
#define LT_SET 11
|
||||||
#define LT_CLEAR 12
|
#define LT_CLEAR 12
|
||||||
|
#define LT_SHUTDOWN 13
|
||||||
|
#define LT_RESTART 14
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1092,40 +1092,6 @@ restart_service(DCB *dcb, SERVICE *service)
|
|||||||
serviceRestart(service);
|
serviceRestart(service);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct {
|
|
||||||
char *str;
|
|
||||||
unsigned int bit;
|
|
||||||
} ServerBits[] = {
|
|
||||||
{ "running", SERVER_RUNNING },
|
|
||||||
{ "master", SERVER_MASTER },
|
|
||||||
{ "slave", SERVER_SLAVE },
|
|
||||||
{ "synced", SERVER_JOINED },
|
|
||||||
{ "ndb", SERVER_NDB },
|
|
||||||
{ "maintenance", SERVER_MAINT },
|
|
||||||
{ "maint", SERVER_MAINT },
|
|
||||||
{ NULL, 0 }
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Map the server status bit
|
|
||||||
*
|
|
||||||
* @param str String representation
|
|
||||||
* @return bit value or 0 on error
|
|
||||||
*/
|
|
||||||
static unsigned int
|
|
||||||
server_map_status(char *str)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; ServerBits[i].str; i++)
|
|
||||||
{
|
|
||||||
if (!strcasecmp(str, ServerBits[i].str))
|
|
||||||
{
|
|
||||||
return ServerBits[i].bit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the status bit of a server
|
* Set the status bit of a server
|
||||||
*
|
*
|
||||||
|
@ -57,6 +57,8 @@ static int maxinfo_pattern_match(char *pattern, char *str);
|
|||||||
static void exec_flush(DCB *dcb, MAXINFO_TREE *tree);
|
static void exec_flush(DCB *dcb, MAXINFO_TREE *tree);
|
||||||
static void exec_set(DCB *dcb, MAXINFO_TREE *tree);
|
static void exec_set(DCB *dcb, MAXINFO_TREE *tree);
|
||||||
static void exec_clear(DCB *dcb, MAXINFO_TREE *tree);
|
static void exec_clear(DCB *dcb, MAXINFO_TREE *tree);
|
||||||
|
static void exec_shutdown(DCB *dcb, MAXINFO_TREE *tree);
|
||||||
|
static void exec_restart(DCB *dcb, MAXINFO_TREE *tree);
|
||||||
void maxinfo_send_ok(DCB *dcb);
|
void maxinfo_send_ok(DCB *dcb);
|
||||||
/**
|
/**
|
||||||
* Execute a parse tree and return the result set or runtime error
|
* Execute a parse tree and return the result set or runtime error
|
||||||
@ -85,6 +87,12 @@ maxinfo_execute(DCB *dcb, MAXINFO_TREE *tree)
|
|||||||
case MAXOP_CLEAR:
|
case MAXOP_CLEAR:
|
||||||
exec_clear(dcb, tree);
|
exec_clear(dcb, tree);
|
||||||
break;
|
break;
|
||||||
|
case MAXOP_SHUTDOWN:
|
||||||
|
exec_shutdown(dcb, tree);
|
||||||
|
break;
|
||||||
|
case MAXOP_RESTART:
|
||||||
|
exec_restart(dcb, tree);
|
||||||
|
break;
|
||||||
|
|
||||||
case MAXOP_TABLE:
|
case MAXOP_TABLE:
|
||||||
case MAXOP_COLUMNS:
|
case MAXOP_COLUMNS:
|
||||||
@ -503,6 +511,236 @@ exec_clear(DCB *dcb, MAXINFO_TREE *tree)
|
|||||||
skygw_log_write(LE, errmsg);
|
skygw_log_write(LE, errmsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern void shutdown_server();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MaxScale shutdown
|
||||||
|
* @param dcb Client DCB
|
||||||
|
* @param tree Parse tree
|
||||||
|
*/
|
||||||
|
void exec_shutdown_maxscale(DCB *dcb, MAXINFO_TREE *tree)
|
||||||
|
{
|
||||||
|
shutdown_server();
|
||||||
|
maxinfo_send_ok(dcb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop a monitor
|
||||||
|
* @param dcb Client DCB
|
||||||
|
* @param tree Parse tree
|
||||||
|
*/
|
||||||
|
void exec_shutdown_monitor(DCB *dcb, MAXINFO_TREE *tree)
|
||||||
|
{
|
||||||
|
char errmsg[120];
|
||||||
|
if (tree && tree->value)
|
||||||
|
{
|
||||||
|
MONITOR* monitor = monitor_find(tree->value);
|
||||||
|
if (monitor)
|
||||||
|
{
|
||||||
|
monitorStop(monitor);
|
||||||
|
maxinfo_send_ok(dcb);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (strlen(tree->value) > 80) // Prevent buffer overrun
|
||||||
|
{
|
||||||
|
tree->value[80] = 0;
|
||||||
|
}
|
||||||
|
sprintf(errmsg, "Invalid argument '%s'", tree->value);
|
||||||
|
maxinfo_send_error(dcb, 0, errmsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sprintf(errmsg, "Missing argument for 'SHUTDOWN MONITOR'");
|
||||||
|
maxinfo_send_error(dcb, 0, errmsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop a service
|
||||||
|
* @param dcb Client DCB
|
||||||
|
* @param tree Parse tree
|
||||||
|
*/
|
||||||
|
void exec_shutdown_service(DCB *dcb, MAXINFO_TREE *tree)
|
||||||
|
{
|
||||||
|
char errmsg[120];
|
||||||
|
if (tree && tree->value)
|
||||||
|
{
|
||||||
|
SERVICE* service = service_find(tree->value);
|
||||||
|
if (service)
|
||||||
|
{
|
||||||
|
serviceStop(service);
|
||||||
|
maxinfo_send_ok(dcb);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (strlen(tree->value) > 80) // Prevent buffer overrun
|
||||||
|
{
|
||||||
|
tree->value[80] = 0;
|
||||||
|
}
|
||||||
|
sprintf(errmsg, "Invalid argument '%s'", tree->value);
|
||||||
|
maxinfo_send_error(dcb, 0, errmsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sprintf(errmsg, "Missing argument for 'SHUTDOWN SERVICE'");
|
||||||
|
maxinfo_send_error(dcb, 0, errmsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The table of shutdown commands that are supported
|
||||||
|
*/
|
||||||
|
static struct
|
||||||
|
{
|
||||||
|
char *name;
|
||||||
|
void (*func)(DCB *, MAXINFO_TREE *);
|
||||||
|
} shutdown_commands[] = {
|
||||||
|
{ "maxscale", exec_shutdown_maxscale},
|
||||||
|
{ "monitor", exec_shutdown_monitor},
|
||||||
|
{ "service", exec_shutdown_service},
|
||||||
|
{ NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a shutdown command parse tree and return OK or runtime error
|
||||||
|
*
|
||||||
|
* @param dcb The DCB that connects to the client
|
||||||
|
* @param tree The parse tree for the query
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
exec_shutdown(DCB *dcb, MAXINFO_TREE *tree)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
char errmsg[120];
|
||||||
|
|
||||||
|
for (i = 0; shutdown_commands[i].name; i++)
|
||||||
|
{
|
||||||
|
if (strcasecmp(shutdown_commands[i].name, tree->value) == 0)
|
||||||
|
{
|
||||||
|
(*shutdown_commands[i].func)(dcb, tree->right);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (strlen(tree->value) > 80) // Prevent buffer overrun
|
||||||
|
{
|
||||||
|
tree->value[80] = 0;
|
||||||
|
}
|
||||||
|
sprintf(errmsg, "Unsupported shutdown command '%s'", tree->value);
|
||||||
|
maxinfo_send_error(dcb, 0, errmsg);
|
||||||
|
skygw_log_write(LE, errmsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restart a monitor
|
||||||
|
* @param dcb Client DCB
|
||||||
|
* @param tree Parse tree
|
||||||
|
*/
|
||||||
|
void exec_restart_monitor(DCB *dcb, MAXINFO_TREE *tree)
|
||||||
|
{
|
||||||
|
char errmsg[120];
|
||||||
|
if (tree && tree->value)
|
||||||
|
{
|
||||||
|
MONITOR* monitor = monitor_find(tree->value);
|
||||||
|
if (monitor)
|
||||||
|
{
|
||||||
|
monitorStart(monitor, NULL);
|
||||||
|
maxinfo_send_ok(dcb);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (strlen(tree->value) > 80) // Prevent buffer overrun
|
||||||
|
{
|
||||||
|
tree->value[80] = 0;
|
||||||
|
}
|
||||||
|
sprintf(errmsg, "Invalid argument '%s'", tree->value);
|
||||||
|
maxinfo_send_error(dcb, 0, errmsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sprintf(errmsg, "Missing argument for 'RESTART MONITOR'");
|
||||||
|
maxinfo_send_error(dcb, 0, errmsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restart a service
|
||||||
|
* @param dcb Client DCB
|
||||||
|
* @param tree Parse tree
|
||||||
|
*/
|
||||||
|
void exec_restart_service(DCB *dcb, MAXINFO_TREE *tree)
|
||||||
|
{
|
||||||
|
char errmsg[120];
|
||||||
|
if (tree && tree->value)
|
||||||
|
{
|
||||||
|
SERVICE* service = service_find(tree->value);
|
||||||
|
if (service)
|
||||||
|
{
|
||||||
|
serviceRestart(service);
|
||||||
|
maxinfo_send_ok(dcb);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (strlen(tree->value) > 80) // Prevent buffer overrun
|
||||||
|
{
|
||||||
|
tree->value[80] = 0;
|
||||||
|
}
|
||||||
|
sprintf(errmsg, "Invalid argument '%s'", tree->value);
|
||||||
|
maxinfo_send_error(dcb, 0, errmsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sprintf(errmsg, "Missing argument for 'RESTART SERVICE'");
|
||||||
|
maxinfo_send_error(dcb, 0, errmsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The table of restart commands that are supported
|
||||||
|
*/
|
||||||
|
static struct
|
||||||
|
{
|
||||||
|
char *name;
|
||||||
|
void (*func)(DCB *, MAXINFO_TREE *);
|
||||||
|
} restart_commands[] = {
|
||||||
|
{ "monitor", exec_restart_monitor},
|
||||||
|
{ "service", exec_restart_service},
|
||||||
|
{ NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a restart command parse tree and return OK or runtime error
|
||||||
|
*
|
||||||
|
* @param dcb The DCB that connects to the client
|
||||||
|
* @param tree The parse tree for the query
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
exec_restart(DCB *dcb, MAXINFO_TREE *tree)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
char errmsg[120];
|
||||||
|
|
||||||
|
for (i = 0; restart_commands[i].name; i++)
|
||||||
|
{
|
||||||
|
if (strcasecmp(restart_commands[i].name, tree->value) == 0)
|
||||||
|
{
|
||||||
|
(*restart_commands[i].func)(dcb, tree->right);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (strlen(tree->value) > 80) // Prevent buffer overrun
|
||||||
|
{
|
||||||
|
tree->value[80] = 0;
|
||||||
|
}
|
||||||
|
sprintf(errmsg, "Unsupported restart command '%s'", tree->value);
|
||||||
|
maxinfo_send_error(dcb, 0, errmsg);
|
||||||
|
skygw_log_write(LE, errmsg);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the current MaxScale version
|
* Return the current MaxScale version
|
||||||
*
|
*
|
||||||
|
@ -117,6 +117,50 @@ MAXINFO_TREE *col, *table;
|
|||||||
ptr = fetch_token(ptr, &token, &text);
|
ptr = fetch_token(ptr, &token, &text);
|
||||||
return make_tree_node(MAXOP_FLUSH, text, NULL, NULL);
|
return make_tree_node(MAXOP_FLUSH, text, NULL, NULL);
|
||||||
|
|
||||||
|
case LT_SHUTDOWN:
|
||||||
|
free(text);
|
||||||
|
ptr = fetch_token(ptr, &token, &text);
|
||||||
|
tree = make_tree_node(MAXOP_SHUTDOWN, text, NULL, NULL);
|
||||||
|
|
||||||
|
if ((ptr = fetch_token(ptr, &token, &text)) == NULL)
|
||||||
|
{
|
||||||
|
/** Possibly SHUTDOWN MAXSCALE */
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
tree->right = make_tree_node(MAXOP_LITERAL, text, NULL, NULL);
|
||||||
|
|
||||||
|
if ((ptr = fetch_token(ptr, &token, &text)) != NULL)
|
||||||
|
{
|
||||||
|
/** Unknown token after SHUTDOWN MONITOR|SERVICE */
|
||||||
|
*parse_error = PARSE_SYNTAX_ERROR;
|
||||||
|
free_tree(tree);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return tree;
|
||||||
|
|
||||||
|
case LT_RESTART:
|
||||||
|
free(text);
|
||||||
|
ptr = fetch_token(ptr, &token, &text);
|
||||||
|
tree = make_tree_node(MAXOP_RESTART, text, NULL, NULL);
|
||||||
|
|
||||||
|
if ((ptr = fetch_token(ptr, &token, &text)) == NULL)
|
||||||
|
{
|
||||||
|
/** Missing token for RESTART MONITOR|SERVICE */
|
||||||
|
*parse_error = PARSE_SYNTAX_ERROR;
|
||||||
|
free_tree(tree);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
tree->right = make_tree_node(MAXOP_LITERAL, text, NULL, NULL);
|
||||||
|
|
||||||
|
if ((ptr = fetch_token(ptr, &token, &text)) != NULL)
|
||||||
|
{
|
||||||
|
/** Unknown token after RESTART MONITOR|SERVICE */
|
||||||
|
*parse_error = PARSE_SYNTAX_ERROR;
|
||||||
|
free_tree(tree);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return tree;
|
||||||
|
|
||||||
case LT_SET:
|
case LT_SET:
|
||||||
free(text); // not needed
|
free(text); // not needed
|
||||||
ptr = fetch_token(ptr, &token, &text);
|
ptr = fetch_token(ptr, &token, &text);
|
||||||
@ -249,21 +293,24 @@ free_tree(MAXINFO_TREE *tree)
|
|||||||
/**
|
/**
|
||||||
* The set of keywords known to the tokeniser
|
* The set of keywords known to the tokeniser
|
||||||
*/
|
*/
|
||||||
static struct {
|
static struct
|
||||||
|
{
|
||||||
char *text;
|
char *text;
|
||||||
int token;
|
int token;
|
||||||
} keywords[] = {
|
} keywords[] = {
|
||||||
{ "show", LT_SHOW },
|
{ "show", LT_SHOW},
|
||||||
{ "select", LT_SELECT },
|
{ "select", LT_SELECT},
|
||||||
{ "from", LT_FROM },
|
{ "from", LT_FROM},
|
||||||
{ "like", LT_LIKE },
|
{ "like", LT_LIKE},
|
||||||
{ "=", LT_EQUAL },
|
{ "=", LT_EQUAL},
|
||||||
{ ",", LT_COMMA },
|
{ ",", LT_COMMA},
|
||||||
{ "*", LT_STAR },
|
{ "*", LT_STAR},
|
||||||
{ "flush", LT_FLUSH },
|
{ "flush", LT_FLUSH},
|
||||||
{ "set", LT_SET },
|
{ "set", LT_SET},
|
||||||
{ "clear", LT_CLEAR },
|
{ "clear", LT_CLEAR},
|
||||||
{ NULL, 0 }
|
{ "shutdown", LT_SHUTDOWN},
|
||||||
|
{ "restart", LT_RESTART},
|
||||||
|
{ NULL, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user