diff --git a/Documentation/MaxAdmin The MaxScale Administration And Monitoring Client.pdf b/Documentation/MaxAdmin The MaxScale Administration And Monitoring Client.pdf new file mode 100644 index 000000000..9ccd75b42 Binary files /dev/null and b/Documentation/MaxAdmin The MaxScale Administration And Monitoring Client.pdf differ diff --git a/client/maxadmin.c b/client/maxadmin.c index 79161110b..5710cc35a 100644 --- a/client/maxadmin.c +++ b/client/maxadmin.c @@ -25,6 +25,8 @@ * Date Who Description * 13/06/14 Mark Riddoch Initial implementation * 15/06/14 Mark Riddoch Addition of source command + * 26/06/14 Mark Riddoch Fix issue with final OK split across + * multiple reads * * @endverbatim */ @@ -440,7 +442,9 @@ char buf[20]; /** * Send a comamnd using the MaxScaled protocol, display the return data - * on standard output + * on standard output. + * + * Input terminates with a lien containing jsut the text OK * * @param so The socket connect to MaxScale * @param cmd The command to send @@ -450,19 +454,38 @@ static int sendCommand(int so, char *cmd) { char buf[80]; -int i; +int i, j, newline = 0; write(so, cmd, strlen(cmd)); while (1) { if ((i = read(so, buf, 80)) == -1) return 0; - if (i > 1 && buf[i-1] == 'K' && buf[i-2] == 'O') + for (j = 0; j < i; j++) { - write(1, buf, i - 2); - return 1; + if (newline == 1 && buf[j] == 'O') + newline = 2; + else if (newline == 2 && buf[j] == 'K' && j == i - 1) + { + return 1; + } + else if (newline == 2) + { + putchar('O'); + putchar(buf[j]); + newline = 0; + } + else if (buf[j] == '\n' || buf[j] == '\r') + { + putchar(buf[j]); + newline = 1; + } + else + { + putchar(buf[j]); + newline = 0; + } } - write(1, buf, i); } return 1; } diff --git a/server/core/dcb.c b/server/core/dcb.c index 5f4c9fa15..f78edd003 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -1270,6 +1270,41 @@ DCB *dcb; spinlock_release(&dcbspin); } +/** + * Diagnotic routine to print client DCB data in a tabular form. + * + * @param pdcb DCB to print results to + */ +void +dListClients(DCB *pdcb) +{ +DCB *dcb; + + spinlock_acquire(&dcbspin); + dcb = allDCBs; + dcb_printf(pdcb, "Client Connections\n"); + dcb_printf(pdcb, "-----------------+------------+----------------------+------------\n"); + dcb_printf(pdcb, " %-15s | %-10s | %-20s | %s\n", + "Client", "DCB", "Service", "Session"); + dcb_printf(pdcb, "-----------------+------------+----------------------+------------\n"); + while (dcb) + { + if (dcb_isclient(dcb) + && dcb->dcb_role == DCB_ROLE_REQUEST_HANDLER) + { + dcb_printf(pdcb, " %-15s | %10p | %-20s | %10p\n", + (dcb->remote ? dcb->remote : ""), + dcb, (dcb->session->service ? + dcb->session->service->name : ""), + dcb->session); + } + dcb = dcb->next; + } + dcb_printf(pdcb, "-----------------+------------+----------------------+------------\n\n"); + spinlock_release(&dcbspin); +} + + /** * Diagnostic to print a DCB to another DCB * @@ -1281,8 +1316,14 @@ dprintDCB(DCB *pdcb, DCB *dcb) { dcb_printf(pdcb, "DCB: %p\n", (void *)dcb); dcb_printf(pdcb, "\tDCB state: %s\n", gw_dcb_state2string(dcb->state)); + if (dcb->session && dcb->session->service) + dcb_printf(pdcb, "\tService: %s\n", + dcb->session->service->name); if (dcb->remote) dcb_printf(pdcb, "\tConnected to: %s\n", dcb->remote); + if (dcb->user) + dcb_printf(pdcb, "\tUsername: %s\n", + dcb->user); dcb_printf(pdcb, "\tOwning Session: %p\n", dcb->session); if (dcb->writeq) dcb_printf(pdcb, "\tQueued write data: %d\n", gwbuf_length(dcb->writeq)); diff --git a/server/core/load_utils.c b/server/core/load_utils.c index 0cfcd2717..93cdf95f6 100644 --- a/server/core/load_utils.c +++ b/server/core/load_utils.c @@ -379,7 +379,9 @@ MODULES *ptr = registered; : (ptr->info->status == MODULE_BETA_RELEASE ? "Beta" : (ptr->info->status == MODULE_GA - ? "GA" : "Unknown")))); + ? "GA" + : (ptr->info->status == MODULE_EXPERIMENTAL + ? "Experimental" : "Unknown"))))); dcb_printf(dcb, "\n"); ptr = ptr->next; } diff --git a/server/core/monitor.c b/server/core/monitor.c index 2fca9bcac..d7b53f5b7 100644 --- a/server/core/monitor.c +++ b/server/core/monitor.c @@ -73,6 +73,7 @@ MONITOR *mon; return NULL; } mon->handle = (*mon->module->startMonitor)(NULL); + mon->state |= MONITOR_STATE_RUNNING; spinlock_acquire(&monLock); mon->next = allMonitors; allMonitors = mon; @@ -93,6 +94,7 @@ monitor_free(MONITOR *mon) MONITOR *ptr; mon->module->stopMonitor(mon->handle); + mon->state &= ~MONITOR_STATE_RUNNING; spinlock_acquire(&monLock); if (allMonitors == mon) allMonitors = mon->next; @@ -119,6 +121,7 @@ void monitorStart(MONITOR *monitor) { monitor->handle = (*monitor->module->startMonitor)(monitor->handle); + monitor->state |= MONITOR_STATE_RUNNING; } /** @@ -130,6 +133,7 @@ void monitorStop(MONITOR *monitor) { monitor->module->stopMonitor(monitor->handle); + monitor->state &= ~MONITOR_STATE_RUNNING; } /** @@ -200,6 +204,47 @@ MONITOR *ptr; spinlock_release(&monLock); } +/** + * Show a single monitor + * + * @param dcb DCB for printing output + */ +void +monitorShow(DCB *dcb, MONITOR *monitor) +{ + + dcb_printf(dcb, "Monitor: %p\n", monitor); + dcb_printf(dcb, "\tName: %s\n", monitor->name); + if (monitor->module->diagnostics) + monitor->module->diagnostics(dcb, monitor->handle); +} + +/** + * List all the monitors + * + * @param dcb DCB for printing output + */ +void +monitorList(DCB *dcb) +{ +MONITOR *ptr; + + spinlock_acquire(&monLock); + ptr = allMonitors; + dcb_printf(dcb, "+----------------------+---------------------\n"); + dcb_printf(dcb, "| %-20s | Status\n", "Monitor"); + dcb_printf(dcb, "+----------------------+---------------------\n"); + while (ptr) + { + dcb_printf(dcb, "| %-20s | %s\n", ptr->name, + ptr->state & MONITOR_STATE_RUNNING + ? "Running" : "Stopped"); + ptr = ptr->next; + } + dcb_printf(dcb, "+----------------------+---------------------\n"); + spinlock_release(&monLock); +} + /** * Find a monitor by name * @@ -249,6 +294,7 @@ void monitorSetInterval (MONITOR *mon, unsigned long interval) { if (mon->module->setInterval != NULL) { + mon->interval = interval; mon->module->setInterval(mon->handle, interval); } } diff --git a/server/core/server.c b/server/core/server.c index f56b39ded..8d16de41b 100644 --- a/server/core/server.c +++ b/server/core/server.c @@ -295,7 +295,8 @@ SERVER_PARAM *param; } } if (server->node_ts > 0) { - dcb_printf(dcb, "\tLast Repl Heartbeat:\t%lu\n", server->node_ts); + dcb_printf(dcb, "\tLast Repl Heartbeat:\t%s", + asctime(localtime(&server->node_ts))); } if ((param = server->parameters) != NULL) { diff --git a/server/core/service.c b/server/core/service.c index 2e61c44c1..3e558c4f2 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -717,8 +717,8 @@ SERVER *ptr = service->databases; int i; printf("Service %p\n", service); - printf("\tService: %s\n", service->name); - printf("\tRouter: %s (%p)\n", service->routerModule, service->router); + printf("\tService: %s\n", service->name); + printf("\tRouter: %s (%p)\n", service->routerModule, service->router); printf("\tStarted: %s", asctime(localtime(&service->stats.started))); printf("\tBackend databases\n"); while (ptr) @@ -795,13 +795,16 @@ SERVER *server = service->databases; int i; dcb_printf(dcb, "Service %p\n", service); - dcb_printf(dcb, "\tService: %s\n", service->name); - dcb_printf(dcb, "\tRouter: %s (%p)\n", service->routerModule, - service->router); + dcb_printf(dcb, "\tService: %s\n", + service->name); + dcb_printf(dcb, "\tRouter: %s (%p)\n", + service->routerModule, service->router); if (service->router) service->router->diagnostics(service->router_instance, dcb); - dcb_printf(dcb, "\tStarted: %s", + dcb_printf(dcb, "\tStarted: %s", asctime(localtime(&service->stats.started))); + dcb_printf(dcb, "\tRoot user access: %s\n", + service->enable_root ? "Enabled" : "Disabled"); if (service->n_filters) { dcb_printf(dcb, "\tFilter chain: "); @@ -820,10 +823,14 @@ int i; server = server->nextdb; } if (service->weightby) - dcb_printf(dcb, "\tRouting weight parameter: %s\n", service->weightby); - dcb_printf(dcb, "\tUsers data: %p\n", service->users); - dcb_printf(dcb, "\tTotal connections: %d\n", service->stats.n_sessions); - dcb_printf(dcb, "\tCurrently connected: %d\n", service->stats.n_current); + dcb_printf(dcb, "\tRouting weight parameter: %s\n", + service->weightby); + dcb_printf(dcb, "\tUsers data: %p\n", + service->users); + dcb_printf(dcb, "\tTotal connections: %d\n", + service->stats.n_sessions); + dcb_printf(dcb, "\tCurrently connected: %d\n", + service->stats.n_current); } /** diff --git a/server/include/dcb.h b/server/include/dcb.h index 816bc7c5b..6d91b94b1 100644 --- a/server/include/dcb.h +++ b/server/include/dcb.h @@ -281,6 +281,7 @@ void printDCB(DCB *); /* Debug print routine */ void dprintAllDCBs(DCB *); /* Debug to print all DCB in the system */ void dprintDCB(DCB *, DCB *); /* Debug to print a DCB in the system */ void dListDCBs(DCB *); /* List all DCBs in the system */ +void dListClients(DCB *); /* List al the client DCBs */ const char *gw_dcb_state2string(int); /* DCB state to string */ void dcb_printf(DCB *, const char *, ...); /* DCB version of printf */ int dcb_isclient(DCB *); /* the DCB is the client of the session */ diff --git a/server/include/modinfo.h b/server/include/modinfo.h index c3c1e64da..bc4107b39 100644 --- a/server/include/modinfo.h +++ b/server/include/modinfo.h @@ -38,7 +38,8 @@ typedef enum { MODULE_IN_DEVELOPMENT = 0, MODULE_ALPHA_RELEASE, MODULE_BETA_RELEASE, - MODULE_GA + MODULE_GA, + MODULE_EXPERIMENTAL } MODULE_STATUS; /** diff --git a/server/include/monitor.h b/server/include/monitor.h index 861c1c070..d65fd075f 100644 --- a/server/include/monitor.h +++ b/server/include/monitor.h @@ -78,13 +78,21 @@ typedef struct { */ #define MONITOR_VERSION {1, 0, 0} +/** + * Monitor state bit mask values + */ +#define MONITOR_STATE_RUNNING 0x0001 + + /** * Representation of the running monitor. */ typedef struct monitor { char *name; /**< The name of the monitor module */ + unsigned int state; /**< The monitor status */ MONITOR_OBJECT *module; /**< The "monitor object" */ void *handle; /**< Handle returned from startMonitor */ + int interval; /**< The monitor interval */ struct monitor *next; /**< Next monitor in the linked list */ } MONITOR; @@ -97,6 +105,8 @@ extern void monitorStop(MONITOR *); extern void monitorStart(MONITOR *); extern void monitorStopAll(); extern void monitorShowAll(DCB *); +extern void monitorShow(DCB *, MONITOR *); +extern void monitorList(DCB *); extern void monitorSetId(MONITOR *, unsigned long); extern void monitorSetInterval (MONITOR *, unsigned long); extern void monitorSetReplicationHeartbeat(MONITOR *, int); diff --git a/server/modules/filter/qlafilter.c b/server/modules/filter/qlafilter.c index bc3e4622d..818c1a6f1 100644 --- a/server/modules/filter/qlafilter.c +++ b/server/modules/filter/qlafilter.c @@ -390,11 +390,24 @@ struct timeval tv; static void diagnostic(FILTER *instance, void *fsession, DCB *dcb) { +QLA_INSTANCE *my_instance = (QLA_INSTANCE *)instance; QLA_SESSION *my_session = (QLA_SESSION *)fsession; if (my_session) { - dcb_printf(dcb, "\t\tLogging to file %s.\n", + dcb_printf(dcb, "\t\tLogging to file %s.\n", my_session->filename); } + if (my_instance->source) + dcb_printf(dcb, "\t\tLimit logging to connections from %s\n", + my_instance->source); + if (my_instance->userName) + dcb_printf(dcb, "\t\tLimit logging to user %s\n", + my_instance->userName); + if (my_instance->match) + dcb_printf(dcb, "\t\tInclude queries that match %s\n", + my_instance->match); + if (my_instance->nomatch) + dcb_printf(dcb, "\t\tExclude queries that match %s\n", + my_instance->nomatch); } diff --git a/server/modules/filter/regexfilter.c b/server/modules/filter/regexfilter.c index 69c00c930..aae2dfa3a 100644 --- a/server/modules/filter/regexfilter.c +++ b/server/modules/filter/regexfilter.c @@ -345,6 +345,14 @@ REGEX_SESSION *my_session = (REGEX_SESSION *)fsession; dcb_printf(dcb, "\t\tNo. of queries altered by filter: %d\n", my_session->replacements); } + if (my_instance->source) + dcb_printf(dcb, + "\t\tReplacement limited to connections from %s\n", + my_instance->source); + if (my_instance->user) + dcb_printf(dcb, + "\t\tReplacement limit to user %s\n", + my_instance->user); } /** diff --git a/server/modules/filter/tee.c b/server/modules/filter/tee.c index 211fbbd70..06dc39251 100644 --- a/server/modules/filter/tee.c +++ b/server/modules/filter/tee.c @@ -118,7 +118,8 @@ typedef struct { int active; /* filter is active? */ DCB *branch_dcb; /* Client DCB for "branch" service */ SESSION *branch_session;/* The branch service session */ - int n_duped; /* Number of duplicated querise */ + int n_duped; /* Number of duplicated queries */ + int n_rejected; /* Number of rejected queries */ int residual; /* Any outstanding SQL text */ } TEE_SESSION; @@ -418,6 +419,10 @@ GWBUF *clone = NULL; my_session->n_duped++; SESSION_ROUTE_QUERY(my_session->branch_session, clone); } + else + { + my_session->n_rejected++; + } return rval; } @@ -435,11 +440,28 @@ GWBUF *clone = NULL; static void diagnostic(FILTER *instance, void *fsession, DCB *dcb) { +TEE_INSTANCE *my_instance = (TEE_INSTANCE *)instance; TEE_SESSION *my_session = (TEE_SESSION *)fsession; + if (my_instance->source) + dcb_printf(dcb, "\t\tLimit to connections from %s\n", + my_instance->source); + dcb_printf(dcb, "\t\tDuplicate statements to service %s\n", + my_instance->service->name); + if (my_instance->userName) + dcb_printf(dcb, "\t\tLimit to user %s\n", + my_instance->userName); + if (my_instance->match) + dcb_printf(dcb, "\t\tInclude queries that match %s\n", + my_instance->match); + if (my_instance->nomatch) + dcb_printf(dcb, "\t\tExclude queries that match %s\n", + my_instance->nomatch); if (my_session) { dcb_printf(dcb, "\t\tNo. of statements duplicated: %d.\n", my_session->n_duped); + dcb_printf(dcb, "\t\tNo. of statements rejected: %d.\n", + my_session->n_rejected); } } diff --git a/server/modules/filter/topfilter.c b/server/modules/filter/topfilter.c index cc13ed6c4..62781fe22 100644 --- a/server/modules/filter/topfilter.c +++ b/server/modules/filter/topfilter.c @@ -539,11 +539,40 @@ int i, inserted; static void diagnostic(FILTER *instance, void *fsession, DCB *dcb) { +TOPN_INSTANCE *my_instance = (TOPN_INSTANCE *)instance; TOPN_SESSION *my_session = (TOPN_SESSION *)fsession; +int i; + dcb_printf(dcb, "\t\tReport size %d\n", + my_instance->topN); + if (my_instance->source) + dcb_printf(dcb, "\t\tLimit logging to connections from %s\n", + my_instance->source); + if (my_instance->user) + dcb_printf(dcb, "\t\tLimit logging to user %s\n", + my_instance->user); + if (my_instance->match) + dcb_printf(dcb, "\t\tInclude queries that match %s\n", + my_instance->match); + if (my_instance->exclude) + dcb_printf(dcb, "\t\tExclude queries that match %s\n", + my_instance->exclude); if (my_session) { dcb_printf(dcb, "\t\tLogging to file %s.\n", my_session->filename); + dcb_printf(dcb, "\t\tCurrent Top %d:\n", my_instance->topN); + for (i = 0; i < my_instance->topN; i++) + { + if (my_session->top[i]->sql) + { + dcb_printf(dcb, "\t\t%d place:\n", i + 1); + dcb_printf(dcb, "\t\t\tExecution time: %.3f seconds\n", + (double)((my_session->top[i]->duration.tv_sec * 1000) + + (my_session->top[i]->duration.tv_usec / 1000)) / 1000); + dcb_printf(dcb, "\t\t\tSQL: %s\n", + my_session->top[i]->sql); + } + } } } diff --git a/server/modules/routing/debugcmd.c b/server/modules/routing/debugcmd.c index fb638d541..a9a5da12a 100644 --- a/server/modules/routing/debugcmd.c +++ b/server/modules/routing/debugcmd.c @@ -128,6 +128,10 @@ struct subcommand showoptions[] = { "Show all currently loaded modules", "Show all currently loaded modules", {0, 0, 0} }, + { "monitor", 1, monitorShow, + "Show the monitor details", + "Show the monitor details", + {ARG_TYPE_MONITOR, 0, 0} }, { "monitors", 0, monitorShowAll, "Show the monitors that are configured", "Show the monitors that are configured", @@ -168,6 +172,10 @@ struct subcommand showoptions[] = { * The subcommands of the list command */ struct subcommand listoptions[] = { + { "clients", 0, dListClients, + "List all the client connections to MaxScale", + "List all the client connections to MaxScale", + {0, 0, 0} }, { "dcbs", 0, dListDCBs, "List all the DCBs active within MaxScale", "List all the DCBs active within MaxScale", @@ -181,8 +189,12 @@ struct subcommand listoptions[] = { "List all the listeners defined within MaxScale", {0, 0, 0} }, { "modules", 0, dprintAllModules, - "Show all currently loaded modules", - "Show all currently loaded modules", + "List all currently loaded modules", + "List all currently loaded modules", + {0, 0, 0} }, + { "monitors", 0, monitorList, + "List all monitors", + "List all monitors", {0, 0, 0} }, { "services", 0, dListServices, "List all the services defined within MaxScale", @@ -300,18 +312,30 @@ struct subcommand reloadoptions[] = { { "dbusers", 1, reload_dbusers, "Reload the dbuser data for a service. E.g. reload dbusers \"splitter service\"", "Reload the dbuser data for a service. E.g. reload dbusers 0x849420", - {ARG_TYPE_DBUSERS, 0, 0} }, + {ARG_TYPE_SERVICE, 0, 0} }, { NULL, 0, NULL, NULL, NULL, {0, 0, 0} } }; static void enable_log_action(DCB *, char *); static void disable_log_action(DCB *, char *); +static void enable_monitor_replication_heartbeat(DCB *dcb, MONITOR *monitor); +static void disable_monitor_replication_heartbeat(DCB *dcb, MONITOR *monitor); +static void enable_service_root(DCB *dcb, SERVICE *service); +static void disable_service_root(DCB *dcb, SERVICE *service); /** * * The subcommands of the enable command * */ struct subcommand enableoptions[] = { + { + "heartbeat", + 1, + enable_monitor_replication_heartbeat, + "Enable the monitor replication heartbeat, pass a monitor name as argument", + "Enable the monitor replication heartbeat, pass a monitor name as argument", + {ARG_TYPE_MONITOR, 0, 0} + }, { "log", 1, @@ -322,6 +346,14 @@ struct subcommand enableoptions[] = { "message E.g. enable log message.", {ARG_TYPE_STRING, 0, 0} }, + { + "root", + 1, + enable_service_root, + "Enable root access to a service, pass a service name to enable root access", + "Enable root access to a service, pass a service name to enable root access", + {ARG_TYPE_SERVICE, 0, 0} + }, { NULL, 0, @@ -337,24 +369,40 @@ struct subcommand enableoptions[] = { * * The subcommands of the disable command * */ struct subcommand disableoptions[] = { - { - "log", - 1, - disable_log_action, - "Disable Log for MaxScale, Options: debug | trace | error | message " - "E.g. disable log debug", - "Disable Log for MaxScale, Options: debug | trace | error | message " - "E.g. disable log debug", - {ARG_TYPE_STRING, 0, 0} - }, - { + { + "heartbeat", + 1, + disable_monitor_replication_heartbeat, + "Disable the monitor replication heartbeat", + "Disable the monitor replication heartbeat", + {ARG_TYPE_MONITOR, 0, 0} + }, + { + "log", + 1, + disable_log_action, + "Disable Log for MaxScale, Options: debug | trace | error | message " + "E.g. disable log debug", + "Disable Log for MaxScale, Options: debug | trace | error | message " + "E.g. disable log debug", + {ARG_TYPE_STRING, 0, 0} + }, + { + "root", + 1, + disable_service_root, + "Disable root access to a service", + "Disable root access to a service", + {ARG_TYPE_SERVICE, 0, 0} + }, + { NULL, 0, NULL, NULL, NULL, {0, 0, 0} - } + } }; #if defined(SS_DEBUG) @@ -850,7 +898,7 @@ unsigned int bitvalue; static void reload_dbusers(DCB *dcb, SERVICE *service) { - dcb_printf(dcb, "Loaded %d database users for server %s.\n", + dcb_printf(dcb, "Loaded %d database users for service %s.\n", reload_mysql_users(service), service->name); } @@ -958,6 +1006,55 @@ restart_monitor(DCB *dcb, MONITOR *monitor) monitorStart(monitor); } +/** + * Enable replication heartbeat for a monitor + * + * @param dcb Connection to user interface + * @param monitor The monitor + */ +static void +enable_monitor_replication_heartbeat(DCB *dcb, MONITOR *monitor) +{ + monitorSetReplicationHeartbeat(monitor, 1); +} + +/** + * Disable replication heartbeat for a monitor + * + * @param dcb Connection to user interface + * @param monitor The monitor + */ +static void +disable_monitor_replication_heartbeat(DCB *dcb, MONITOR *monitor) +{ + monitorSetReplicationHeartbeat(monitor, 0); +} + +/** + * Enable root access to a service + * + * @param dcb Connection to user interface + * @param service The service + */ +static void +enable_service_root(DCB *dcb, SERVICE *service) +{ + serviceEnableRootUser(service, 1); +} + +/** + * Disable root access to a service + * + * @param dcb Connection to user interface + * @param service The service + */ +static void +disable_service_root(DCB *dcb, SERVICE *service) +{ + serviceEnableRootUser(service, 0); +} + + /** * The log enable action */