mirror of
https://git.proxmox.com/git/mirror_corosync
synced 2025-10-05 03:13:28 +00:00

1. Enable scripting of vqsim and add man page I've added a 'sleep' command to help with scripting as well as documentation on how to do it. 2. Make 'sync' operation much more robust and useful Refactored a lot of code to make sure that in sync mode the prompt appears at the 'right' time. What we do is wait for all of the nodes in all partitions to have the same ring_id. If this doesn't happen then the timeout will fire as before. 3. Rename binary to corosync-vqsim and add a sub-package for it Signed-off-by: Christine Caulfield <ccaulfie@redhat.com> Reviewed-by: Jan Friesse <jfriesse@redhat.com>
419 lines
8.8 KiB
C
419 lines
8.8 KiB
C
/* Parses the interactive commands */
|
|
|
|
#include <config.h>
|
|
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <netinet/in.h>
|
|
#ifdef HAVE_READLINE_HISTORY_H
|
|
#include <readline/history.h>
|
|
#endif
|
|
|
|
#include <corosync/coroapi.h>
|
|
#include "vqsim.h"
|
|
|
|
static void do_usage(void)
|
|
{
|
|
printf(" All node IDs in the cluster are unique and belong to a numbered 'partition' (default=0)\n");
|
|
printf("\n");
|
|
printf("up [<partition>:][<nodeid>[,<nodeid>] ...] [[<partition>:][<nodeid>...]] [...]\n");
|
|
printf(" bring node(s) online in the specified partition(s)\n");
|
|
printf("down <nodeid>,[<nodeid>...]\n");
|
|
printf(" send nodes offline (shut them down)\n");
|
|
printf("move/split [<partition>:][<nodeid>[,<nodeid>] ...] [[<partition>:][<nodeid>...]] [...]\n");
|
|
printf(" Move nodes from one partition to another (netsplit)\n");
|
|
printf(" <partition> here is the partition to move the nodes to\n");
|
|
printf("join <partition> <partition> [<partition>] ... \n");
|
|
printf(" Join partitions together (reverse of a netsplit)\n");
|
|
printf("qdevice on|off [<partition>:][<nodeid>[,<nodeid>] ...] [[<partition>:][<nodeid>...]] [...]\n");
|
|
printf(" Enable quorum device in specified nodes\n");
|
|
printf("autofence on|off\n");
|
|
printf(" automatically 'down' nodes on inquorate side on netsplit\n");
|
|
printf("timeout <n> (default 250)\n");
|
|
printf(" Wait a maximum of <n> milli-seconds for the next command to complete.\n");
|
|
printf("sync on|off (default on)\n");
|
|
printf(" enable/disable synchronous execution of commands (wait for completion)\n");
|
|
printf("assert on|off (default off)\n");
|
|
printf(" Abort the simulation run if a timeout expires\n");
|
|
printf("show Show current nodes status\n");
|
|
printf("exit\n\n");
|
|
}
|
|
|
|
|
|
/* Commands return 0 if they return immediately, >1 if we are waiting for replies from nodes */
|
|
typedef int (*cmd_routine_t)(int argc, char **argv);
|
|
|
|
static int run_up_cmd(int argc, char **argv);
|
|
static int run_down_cmd(int argc, char **argv);
|
|
static int run_join_cmd(int argc, char **argv);
|
|
static int run_move_cmd(int argc, char **argv);
|
|
static int run_exit_cmd(int argc, char **argv);
|
|
static int run_show_cmd(int argc, char **argv);
|
|
static int run_timeout_cmd(int argc, char **argv);
|
|
static int run_assert_cmd(int argc, char **argv);
|
|
static int run_autofence_cmd(int argc, char **argv);
|
|
static int run_qdevice_cmd(int argc, char **argv);
|
|
static int run_sync_cmd(int argc, char **argv);
|
|
|
|
static struct cmd_list_struct {
|
|
const char *cmd;
|
|
int min_args;
|
|
cmd_routine_t cmd_runner;
|
|
} cmd_list[] = {
|
|
{ "up", 1, run_up_cmd},
|
|
{ "down", 1, run_down_cmd},
|
|
{ "move", 2, run_move_cmd},
|
|
{ "split", 2, run_move_cmd},
|
|
{ "join", 2, run_join_cmd},
|
|
{ "autofence", 1, run_autofence_cmd},
|
|
{ "qdevice", 1, run_qdevice_cmd},
|
|
{ "show", 0, run_show_cmd},
|
|
{ "timeout", 1, run_timeout_cmd},
|
|
{ "sync", 1, run_sync_cmd},
|
|
{ "assert", 1, run_assert_cmd},
|
|
{ "exit", 0, run_exit_cmd},
|
|
{ "quit", 0, run_exit_cmd},
|
|
{ "q", 0, run_exit_cmd},
|
|
};
|
|
static int num_cmds = (sizeof(cmd_list)) / sizeof(struct cmd_list_struct);
|
|
#define MAX_ARGS 1024
|
|
|
|
/* Takes a <partition>:[<node>[,<node>]...] list and return it
|
|
as a partition and a list of nodes.
|
|
Returns 0 if successful, -1 if not
|
|
*/
|
|
static int parse_partition_nodelist(char *string, int *partition, int *num_nodes, int **retnodes)
|
|
{
|
|
int i;
|
|
int nodecount;
|
|
int len;
|
|
int last_comma;
|
|
char *nodeptr;
|
|
int *nodes;
|
|
char *colonptr = strchr(string, ':');
|
|
|
|
if (colonptr) {
|
|
*colonptr = '\0';
|
|
nodeptr = colonptr+1;
|
|
*partition = atoi(string);
|
|
}
|
|
else {
|
|
/* Default to partition 0 */
|
|
*partition = 0;
|
|
nodeptr = string;
|
|
}
|
|
|
|
/* Count the number of commas and allocate space for the nodes */
|
|
nodecount = 0;
|
|
for (i=0; i<strlen(nodeptr); i++) {
|
|
if (nodeptr[i] == ',') {
|
|
nodecount++;
|
|
}
|
|
}
|
|
nodecount++; /* The one between the last comma and the trailing NUL */
|
|
if (nodecount < 1 || nodecount > MAX_NODES) {
|
|
return -1;
|
|
}
|
|
|
|
nodes = malloc(sizeof(int) * nodecount);
|
|
if (!nodes) {
|
|
return -1;
|
|
}
|
|
|
|
nodecount = 0;
|
|
last_comma = 0;
|
|
len = strlen(nodeptr);
|
|
for (i=0; i<=len; i++) {
|
|
if (nodeptr[i] == ',' || nodeptr[i] == '\0') {
|
|
|
|
nodeptr[i] = '\0';
|
|
nodes[nodecount++] = atoi(&nodeptr[last_comma]);
|
|
last_comma = i+1;
|
|
}
|
|
}
|
|
|
|
*num_nodes = nodecount;
|
|
*retnodes = nodes;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void parse_input_command(char *rl_cmd)
|
|
{
|
|
int i;
|
|
int argc = 0;
|
|
int valid_cmd = 0;
|
|
char *argv[MAX_ARGS];
|
|
int last_arg_start = 0;
|
|
int last_was_space = 0;
|
|
int len;
|
|
int ret = 0;
|
|
char *cmd;
|
|
|
|
/* ^D quits */
|
|
if (rl_cmd == NULL) {
|
|
(void)run_exit_cmd(0, NULL);
|
|
}
|
|
/* '#' starts a comment */
|
|
if (rl_cmd[0] == '#') {
|
|
return;
|
|
}
|
|
|
|
cmd = strdup(rl_cmd);
|
|
|
|
/* Split cmd up into args
|
|
* destroying the original string mwahahahaha
|
|
*/
|
|
|
|
len = strlen(cmd);
|
|
|
|
/* Span leading spaces */
|
|
for (i=0; cmd[i] == ' '; i++)
|
|
;
|
|
last_arg_start = i;
|
|
|
|
for (; i<=len; i++) {
|
|
if (cmd[i] == ' ' || cmd[i] == '\0') {
|
|
|
|
/* Allow multiple spaces */
|
|
if (last_was_space) {
|
|
continue;
|
|
}
|
|
|
|
cmd[i] = '\0';
|
|
last_was_space = 1;
|
|
|
|
argv[argc] = &cmd[last_arg_start];
|
|
argc++;
|
|
}
|
|
else {
|
|
if (last_was_space) {
|
|
last_arg_start = i;
|
|
}
|
|
last_was_space = 0;
|
|
}
|
|
}
|
|
|
|
/* Ignore null commands */
|
|
if (strlen(argv[0]) == 0) {
|
|
free(cmd);
|
|
resume_kb_input(0);
|
|
return;
|
|
}
|
|
#ifdef HAVE_READLINE_HISTORY_H
|
|
add_history(rl_cmd);
|
|
#endif
|
|
|
|
/* Dispatch command */
|
|
for (i=0; i<num_cmds; i++) {
|
|
if (strcasecmp(argv[0], cmd_list[i].cmd) == 0) {
|
|
|
|
if (argc < cmd_list[i].min_args) {
|
|
break;
|
|
}
|
|
ret = cmd_list[i].cmd_runner(argc, argv);
|
|
valid_cmd = 1;
|
|
}
|
|
}
|
|
if (!valid_cmd) {
|
|
do_usage();
|
|
}
|
|
free(cmd);
|
|
|
|
/* ret==0 means we can return immediately to command-line input */
|
|
if (ret == 0) {
|
|
resume_kb_input(ret);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static int run_up_cmd(int argc, char **argv)
|
|
{
|
|
int partition;
|
|
int num_nodes;
|
|
int *nodelist;
|
|
int i,j;
|
|
int succeeded = 0;
|
|
|
|
if (argc <= 1) {
|
|
return 0;
|
|
}
|
|
|
|
cmd_start_sync_command();
|
|
|
|
for (i=1; i<argc; i++) {
|
|
if (parse_partition_nodelist(argv[i], &partition, &num_nodes, &nodelist) == 0) {
|
|
for (j=0; j<num_nodes; j++) {
|
|
if (!cmd_start_new_node(nodelist[j], partition)) {
|
|
succeeded++;
|
|
}
|
|
}
|
|
free(nodelist);
|
|
}
|
|
}
|
|
return succeeded;
|
|
}
|
|
|
|
static int run_down_cmd(int argc, char **argv)
|
|
{
|
|
int nodeid;
|
|
int i;
|
|
int succeeded = 0;
|
|
|
|
cmd_start_sync_command();
|
|
|
|
for (i=1; i<argc; i++) {
|
|
nodeid = atoi(argv[1]);
|
|
if (!cmd_stop_node(nodeid)) {
|
|
succeeded++;
|
|
}
|
|
}
|
|
return succeeded;
|
|
}
|
|
|
|
static int run_join_cmd(int argc, char **argv)
|
|
{
|
|
int i;
|
|
|
|
if (argc < 2) {
|
|
printf("join needs at least two partition numbers\n");
|
|
return 0;
|
|
}
|
|
|
|
cmd_start_sync_command();
|
|
|
|
for (i=2; i<argc; i++) {
|
|
cmd_join_partitions(atoi(argv[1]), atoi(argv[i]));
|
|
}
|
|
cmd_update_all_partitions(1);
|
|
return 1;
|
|
}
|
|
|
|
static int run_move_cmd(int argc, char **argv)
|
|
{
|
|
int i;
|
|
int partition;
|
|
int num_nodes;
|
|
int *nodelist;
|
|
|
|
cmd_start_sync_command();
|
|
|
|
for (i=1; i<argc; i++) {
|
|
if (parse_partition_nodelist(argv[i], &partition, &num_nodes, &nodelist) == 0) {
|
|
cmd_move_nodes(partition, num_nodes, nodelist);
|
|
free(nodelist);
|
|
}
|
|
}
|
|
cmd_update_all_partitions(1);
|
|
return 1;
|
|
}
|
|
|
|
static int run_autofence_cmd(int argc, char **argv)
|
|
{
|
|
int onoff = -1;
|
|
|
|
if (strcasecmp(argv[1], "on") == 0) {
|
|
onoff = 1;
|
|
}
|
|
if (strcasecmp(argv[1], "off") == 0) {
|
|
onoff = 0;
|
|
}
|
|
if (onoff == -1) {
|
|
fprintf(stderr, "ERR: autofence value must be 'on' or 'off'\n");
|
|
}
|
|
else {
|
|
cmd_set_autofence(onoff);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int run_qdevice_cmd(int argc, char **argv)
|
|
{
|
|
int i,j;
|
|
int partition;
|
|
int num_nodes;
|
|
int *nodelist;
|
|
int onoff = -1;
|
|
|
|
if (strcasecmp(argv[1], "on") == 0) {
|
|
onoff = 1;
|
|
}
|
|
if (strcasecmp(argv[1], "off") == 0) {
|
|
onoff = 0;
|
|
}
|
|
|
|
if (onoff == -1) {
|
|
fprintf(stderr, "ERR: qdevice should be 'on' or 'off'\n");
|
|
return 0;
|
|
}
|
|
|
|
for (i=2; i<argc; i++) {
|
|
if (parse_partition_nodelist(argv[i], &partition, &num_nodes, &nodelist) == 0) {
|
|
for (j=0; j<num_nodes; j++) {
|
|
cmd_qdevice_poll(nodelist[j], onoff);
|
|
}
|
|
free(nodelist);
|
|
}
|
|
}
|
|
cmd_update_all_partitions(0);
|
|
return 0;
|
|
}
|
|
|
|
static int run_show_cmd(int argc, char **argv)
|
|
{
|
|
cmd_show_node_states();
|
|
return 0;
|
|
}
|
|
|
|
static int run_timeout_cmd(int argc, char **argv)
|
|
{
|
|
cmd_set_timeout(atol(argv[1]));
|
|
return 0;
|
|
}
|
|
|
|
static int run_sync_cmd(int argc, char **argv)
|
|
{
|
|
int onoff = -1;
|
|
|
|
if (strcasecmp(argv[1], "on") == 0) {
|
|
onoff = 1;
|
|
}
|
|
if (strcasecmp(argv[1], "off") == 0) {
|
|
onoff = 0;
|
|
}
|
|
if (onoff == -1) {
|
|
fprintf(stderr, "ERR: sync value must be 'on' or 'off'\n");
|
|
}
|
|
else {
|
|
cmd_set_sync(onoff);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int run_assert_cmd(int argc, char **argv)
|
|
{
|
|
int onoff = -1;
|
|
|
|
if (strcasecmp(argv[1], "on") == 0) {
|
|
onoff = 1;
|
|
}
|
|
if (strcasecmp(argv[1], "off") == 0) {
|
|
onoff = 0;
|
|
}
|
|
if (onoff == -1) {
|
|
fprintf(stderr, "ERR: assert value must be 'on' or 'off'\n");
|
|
}
|
|
else {
|
|
cmd_set_assert(onoff);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int run_exit_cmd(int argc, char **argv)
|
|
{
|
|
cmd_stop_all_nodes();
|
|
exit(0);
|
|
}
|