Maxinfo now allows users to flush logs and change the server status
through the MySQL interface.
This commit is contained in:
Markus Makela
2015-10-30 21:02:14 +02:00
parent 264944ff23
commit 3ea55e3b50
6 changed files with 347 additions and 3 deletions

View File

@ -67,6 +67,8 @@ Uptime: 72 Threads: 1 Sessions: 11
The SQL command used to interact with maxinfo is the show command, a variety of show commands are available and will be described in the following sections. The SQL command used to interact with maxinfo is the show command, a variety of show commands are available and will be described in the following sections.
Maxinfo also supports the `FLUSH LOGS`, `SET SERVER <name> <status>` and `CLEAR SERVER <name> <status>` commands. These behave the same as their MaxAdmin counterpart.
## Show variables ## Show variables
The show variables command will display a set of name and value pairs for a number of MaxScale system variables. The show variables command will display a set of name and value pairs for a number of MaxScale system variables.

View File

@ -907,3 +907,33 @@ server_update_port(SERVER *server, unsigned short port)
spinlock_release(&server_spin); spinlock_release(&server_spin);
} }
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
*/
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;
}

View File

@ -208,4 +208,5 @@ extern DCB *server_get_persistent(SERVER *, char *, const char *);
extern void server_update_address(SERVER *, char *); extern void server_update_address(SERVER *, char *);
extern void server_update_port(SERVER *, unsigned short); extern void server_update_port(SERVER *, unsigned short);
extern RESULTSET *serverGetList(); extern RESULTSET *serverGetList();
extern unsigned int server_map_status(char *str);
#endif #endif

View File

@ -73,7 +73,10 @@ typedef enum
MAXOP_LITERAL, MAXOP_LITERAL,
MAXOP_PREDICATE, MAXOP_PREDICATE,
MAXOP_LIKE, MAXOP_LIKE,
MAXOP_EQUAL MAXOP_EQUAL,
MAXOP_FLUSH,
MAXOP_SET,
MAXOP_CLEAR
} MAXINFO_OPERATOR; } MAXINFO_OPERATOR;
/** /**
@ -109,6 +112,9 @@ typedef struct maxinfo_tree {
#define LT_FROM 7 #define LT_FROM 7
#define LT_STAR 8 #define LT_STAR 8
#define LT_VARIABLE 9 #define LT_VARIABLE 9
#define LT_FLUSH 10
#define LT_SET 11
#define LT_CLEAR 12
/** /**

View File

@ -54,7 +54,10 @@ static void exec_select(DCB *dcb, MAXINFO_TREE *tree);
static void exec_show_variables(DCB *dcb, MAXINFO_TREE *filter); static void exec_show_variables(DCB *dcb, MAXINFO_TREE *filter);
static void exec_show_status(DCB *dcb, MAXINFO_TREE *filter); static void exec_show_status(DCB *dcb, MAXINFO_TREE *filter);
static int maxinfo_pattern_match(char *pattern, char *str); static int maxinfo_pattern_match(char *pattern, char *str);
static void exec_flush(DCB *dcb, MAXINFO_TREE *tree);
static void exec_set(DCB *dcb, MAXINFO_TREE *tree);
static void exec_clear(DCB *dcb, MAXINFO_TREE *tree);
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
* *
@ -72,6 +75,17 @@ maxinfo_execute(DCB *dcb, MAXINFO_TREE *tree)
case MAXOP_SELECT: case MAXOP_SELECT:
exec_select(dcb, tree); exec_select(dcb, tree);
break; break;
case MAXOP_FLUSH:
exec_flush(dcb, tree);
break;
case MAXOP_SET:
exec_set(dcb, tree);
break;
case MAXOP_CLEAR:
exec_clear(dcb, tree);
break;
case MAXOP_TABLE: case MAXOP_TABLE:
case MAXOP_COLUMNS: case MAXOP_COLUMNS:
case MAXOP_LITERAL: case MAXOP_LITERAL:
@ -274,6 +288,221 @@ char errmsg[120];
LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, errmsg))); LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, errmsg)));
} }
/**
* Flush all logs to disk and rotate them.
* @param dcb The DCB that connects to the client
* @param tree The parse tree for the query
*/
void exec_flush_logs(DCB *dcb, MAXINFO_TREE *tree)
{
skygw_log_rotate(LE);
skygw_log_rotate(LM);
skygw_log_rotate(LT);
skygw_log_rotate(LD);
maxinfo_send_ok(dcb);
}
/**
* The table of flush commands that are supported
*/
static struct
{
char *name;
void (*func)(DCB *, MAXINFO_TREE *);
} flush_commands[] = {
{ "logs", exec_flush_logs},
{ NULL, NULL}
};
/**
* Execute a flush command parse tree and return the result set or runtime error
*
* @param dcb The DCB that connects to the client
* @param tree The parse tree for the query
*/
static void
exec_flush(DCB *dcb, MAXINFO_TREE *tree)
{
int i;
char errmsg[120];
for (i = 0; flush_commands[i].name; i++)
{
if (strcasecmp(flush_commands[i].name, tree->value) == 0)
{
(*flush_commands[i].func)(dcb, tree->right);
return;
}
}
if (strlen(tree->value) > 80) // Prevent buffer overrun
{
tree->value[80] = 0;
}
sprintf(errmsg, "Unsupported flush command '%s'", tree->value);
maxinfo_send_error(dcb, 0, errmsg);
skygw_log_write(LE, errmsg);
}
/**
* Set the server status.
* @param dcb Client DCB
* @param tree Parse tree
*/
void exec_set_server(DCB *dcb, MAXINFO_TREE *tree)
{
SERVER* server = server_find_by_unique_name(tree->value);
char errmsg[120];
if (server)
{
int status = server_map_status(tree->right->value);
if (status != 0)
{
server_set_status(server, status);
maxinfo_send_ok(dcb);
}
else
{
if (strlen(tree->right->value) > 80) // Prevent buffer overrun
{
tree->right->value[80] = 0;
}
sprintf(errmsg, "Invalid argument '%s'", tree->right->value);
maxinfo_send_error(dcb, 0, errmsg);
}
}
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);
}
}
/**
* The table of set commands that are supported
*/
static struct
{
char *name;
void (*func)(DCB *, MAXINFO_TREE *);
} set_commands[] = {
{ "server", exec_set_server},
{ NULL, NULL}
};
/**
* Execute a set command parse tree and return the result set or runtime error
*
* @param dcb The DCB that connects to the client
* @param tree The parse tree for the query
*/
static void
exec_set(DCB *dcb, MAXINFO_TREE *tree)
{
int i;
char errmsg[120];
for (i = 0; set_commands[i].name; i++)
{
if (strcasecmp(set_commands[i].name, tree->value) == 0)
{
(*set_commands[i].func)(dcb, tree->right);
return;
}
}
if (strlen(tree->value) > 80) // Prevent buffer overrun
{
tree->value[80] = 0;
}
sprintf(errmsg, "Unsupported set command '%s'", tree->value);
maxinfo_send_error(dcb, 0, errmsg);
skygw_log_write(LE, errmsg);
}
/**
* Clear the server status.
* @param dcb Client DCB
* @param tree Parse tree
*/
void exec_clear_server(DCB *dcb, MAXINFO_TREE *tree)
{
SERVER* server = server_find_by_unique_name(tree->value);
char errmsg[120];
if (server)
{
int status = server_map_status(tree->right->value);
if (status != 0)
{
server_clear_status(server, status);
maxinfo_send_ok(dcb);
}
else
{
if (strlen(tree->right->value) > 80) // Prevent buffer overrun
{
tree->right->value[80] = 0;
}
sprintf(errmsg, "Invalid argument '%s'", tree->right->value);
maxinfo_send_error(dcb, 0, errmsg);
}
}
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);
}
}
/**
* The table of clear commands that are supported
*/
static struct
{
char *name;
void (*func)(DCB *, MAXINFO_TREE *);
} clear_commands[] = {
{ "server", exec_clear_server},
{ NULL, NULL}
};
/**
* Execute a clear command parse tree and return the result set or runtime error
*
* @param dcb The DCB that connects to the client
* @param tree The parse tree for the query
*/
static void
exec_clear(DCB *dcb, MAXINFO_TREE *tree)
{
int i;
char errmsg[120];
for (i = 0; clear_commands[i].name; i++)
{
if (strcasecmp(clear_commands[i].name, tree->value) == 0)
{
(*clear_commands[i].func)(dcb, tree->right);
return;
}
}
if (strlen(tree->value) > 80) // Prevent buffer overrun
{
tree->value[80] = 0;
}
sprintf(errmsg, "Unsupported clear 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
* *
@ -764,3 +993,25 @@ extern char *strcasestr();
return rval; return rval;
} }
} }
/**
* Send an OK packet to the client.
* @param dcb The DCB that connects to the client
*/
void maxinfo_send_ok(DCB *dcb)
{
static const char ok_packet[] ={
0x07, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00,
0x00, 0x00,
0x00, 0x00
};
GWBUF* buffer = gwbuf_alloc(sizeof(ok_packet));
if (buffer)
{
memcpy(buffer->start, ok_packet, sizeof(ok_packet));
dcb->func.write(dcb, buffer);
}
}

View File

@ -50,7 +50,8 @@ static void free_tree(MAXINFO_TREE *);
static char *fetch_token(char *, int *, char **); static char *fetch_token(char *, int *, char **);
static MAXINFO_TREE *parse_column_list(char **sql); static MAXINFO_TREE *parse_column_list(char **sql);
static MAXINFO_TREE *parse_table_name(char **sql); static MAXINFO_TREE *parse_table_name(char **sql);
MAXINFO_TREE* maxinfo_parse_literals(MAXINFO_TREE *tree, int min_args, char *ptr,
PARSE_ERROR *parse_error);
/** /**
* Parse a SQL subset for the maxinfo plugin and return a parse tree * Parse a SQL subset for the maxinfo plugin and return a parse tree
@ -111,6 +112,23 @@ MAXINFO_TREE *col, *table;
table = parse_table_name(&ptr); table = parse_table_name(&ptr);
return make_tree_node(MAXOP_SELECT, NULL, col, table); return make_tree_node(MAXOP_SELECT, NULL, col, table);
#endif #endif
case LT_FLUSH:
free(text); // not needed
ptr = fetch_token(ptr, &token, &text);
return make_tree_node(MAXOP_FLUSH, text, NULL, NULL);
case LT_SET:
free(text); // not needed
ptr = fetch_token(ptr, &token, &text);
tree = make_tree_node(MAXOP_SET, text, NULL, NULL);
return maxinfo_parse_literals(tree, 2, ptr, parse_error);
case LT_CLEAR:
free(text); // not needed
ptr = fetch_token(ptr, &token, &text);
tree = make_tree_node(MAXOP_CLEAR, text, NULL, NULL);
return maxinfo_parse_literals(tree, 2, ptr, parse_error);
break;
default: default:
*parse_error = PARSE_SYNTAX_ERROR; *parse_error = PARSE_SYNTAX_ERROR;
return NULL; return NULL;
@ -242,6 +260,9 @@ static struct {
{ "=", LT_EQUAL }, { "=", LT_EQUAL },
{ ",", LT_COMMA }, { ",", LT_COMMA },
{ "*", LT_STAR }, { "*", LT_STAR },
{ "flush", LT_FLUSH },
{ "set", LT_SET },
{ "clear", LT_CLEAR },
{ NULL, 0 } { NULL, 0 }
}; };
@ -322,3 +343,36 @@ int i;
*token = LT_STRING; *token = LT_STRING;
return s2; return s2;
} }
/**
* Parse the remaining arguments as literals.
* @param tree Previous head of the parse tree
* @param min_args Minimum required number of arguments
* @param ptr Pointer to client command
* @param parse_error Pointer to parsing error to fill
* @return Parsed tree or NULL if parsing failed
*/
MAXINFO_TREE* maxinfo_parse_literals(MAXINFO_TREE *tree, int min_args, char *ptr,
PARSE_ERROR *parse_error)
{
int token;
MAXINFO_TREE* node = tree;
char *text;
for(int i = 0; i < min_args; i++)
{
if((ptr = fetch_token(ptr, &token, &text)) == NULL ||
(node->right = make_tree_node(MAXOP_LITERAL, text, NULL, NULL)) == NULL)
{
*parse_error = PARSE_SYNTAX_ERROR;
free_tree(tree);
if(ptr)
{
free(text);
}
return NULL;
}
node = node->right;
}
return tree;
}