cluster: RHEL6 - fence_node/libfence: status

Fabio M. Di Nitto fabbione at fedoraproject.org
Tue Sep 11 18:07:52 UTC 2012


Gitweb:        http://git.fedorahosted.org/git/?p=cluster.git;a=commitdiff;h=ea6bcc00c3246381060e882ba40dccd7238b205a
Commit:        ea6bcc00c3246381060e882ba40dccd7238b205a
Parent:        6b7602b0f65268e2f09c87a314cda3947d839b35
Author:        David Teigland <teigland at redhat.com>
AuthorDate:    Mon Aug 27 14:13:10 2012 -0500
Committer:     Fabio M. Di Nitto <fdinitto at redhat.com>
CommitterDate: Tue Sep 11 20:07:18 2012 +0200

fence_node/libfence: status

fence_node -S to run status on a node, via
libfence fence_node_status().

Resolves: rhbz#797952

Signed-off-by: David Teigland <teigland at redhat.com>
Signed-off-by: Fabio M. Di Nitto <fdinitto at redhat.com>
---
 fence/fence_node/fence_node.c |   71 ++++++-
 fence/libfence/agent.c        |  461 +++++++++++++++++++++++++++++++++++++++++
 fence/libfence/libfence.h     |   26 +++
 3 files changed, 550 insertions(+), 8 deletions(-)

diff --git a/fence/fence_node/fence_node.c b/fence/fence_node/fence_node.c
index a98962a..f926825 100644
--- a/fence/fence_node/fence_node.c
+++ b/fence/fence_node/fence_node.c
@@ -14,13 +14,16 @@ static char *prog_name;
 static char our_name[CMAN_MAX_NODENAME_LEN+1];
 static int verbose;
 static int unfence;
+static int status;
+static int call_fenced = 1;
+static int use_method_num;
 
 #define FL_SIZE 32
 static struct fence_log flog[FL_SIZE];
 static int flog_count;
 static const char *action = "fence";
 
-#define OPTION_STRING "UvhV"
+#define OPTION_STRING "UvhVSe:m:"
 
 #define die(fmt, args...) \
 do \
@@ -39,10 +42,13 @@ static void print_usage(void)
 	printf("\n");
 	printf("Options:\n");
 	printf("\n");
-	printf("  -U    Unfence the node, default local node name\n");
-	printf("  -v    Show fence agent results, -vv for agent args\n");
-	printf("  -h    Print this help, then exit\n");
-	printf("  -V    Print program version information, then exit\n");
+	printf("  -U       Unfence the node, default local node name\n");
+	printf("  -S       Run status on node name\n");
+	printf("  -v       Show fence agent results, -vv for agent args\n");
+	printf("  -h       Print this help, then exit\n");
+	printf("  -V       Print program version information, then exit\n");
+	printf("  -e 0|1   Enable/disable fenced_external notification\n");
+	printf("  -m <num> Method number, starting from 1\n");
 	printf("\n");
 }
 
@@ -104,6 +110,14 @@ static const char *fe_str(int r)
 		return "error config method";
 	case FE_READ_DEVICE:
 		return "error config device";
+	case FE_NUM_METHOD:
+		return "error method number";
+	case FE_AGENT_STATUS_ON:
+		return "status on";
+	case FE_AGENT_STATUS_OFF:
+		return "status off";
+	case FE_AGENT_STATUS_ERROR:
+		return "status error";
 	default:
 		return "error unknown";
 	}
@@ -126,6 +140,19 @@ int main(int argc, char *argv[])
 			action = "unfence";
 			break;
 
+		case 'S':
+			status = 1;
+			action = "status";
+			break;
+
+		case 'e':
+			call_fenced = atoi(optarg);
+			break;
+
+		case 'm':
+			use_method_num = atoi(optarg);
+			break;
+
 		case 'v':
 			verbose++;
 			break;
@@ -178,11 +205,17 @@ int main(int argc, char *argv[])
 	memset(&flog, 0, sizeof(flog));
 	flog_count = 0;
 
-	if (unfence)
+	if (status)
+		error = fence_node_status(victim, flog, FL_SIZE, &flog_count,
+					  use_method_num);
+	else if (unfence)
 		error = unfence_node(victim, flog, FL_SIZE, &flog_count);
 	else
 		error = fence_node(victim, flog, FL_SIZE, &flog_count);
 
+	if (status && !verbose && error < 0)
+		verbose = 1;
+
 	if (!verbose)
 		goto skip;
 
@@ -211,7 +244,28 @@ int main(int argc, char *argv[])
 	logt_init("fence_node", LOG_MODE_OUTPUT_SYSLOG, SYSLOGFACILITY,
 		  SYSLOGLEVEL, 0, NULL);
 
-	if (unfence) {
+	if (status) {
+		if (error == -2) {
+			fprintf(stderr, "status %s undefined\n", victim);
+			rv = 2;
+		} else if (error < 0) {
+			fprintf(stderr, "status %s failed %d\n", victim, error);
+			logt_print(LOG_ERR, "status %s failed %d\n", victim, error);
+			rv = EXIT_FAILURE;
+		} else if (error == 2) {
+			fprintf(stderr, "status %s success off\n", victim);
+			logt_print(LOG_ERR, "status %s success off\n", victim);
+			rv = EXIT_SUCCESS;
+		} else if (!error) {
+			fprintf(stderr, "status %s success on\n", victim);
+			logt_print(LOG_ERR, "status %s success on\n", victim);
+			rv = EXIT_SUCCESS;
+		} else {
+			fprintf(stderr, "status %s failed invalid %d\n", victim, error);
+			logt_print(LOG_ERR, "status %s failed invalid %d\n", victim, error);
+			rv = EXIT_FAILURE;
+		}
+	} else if (unfence) {
 		if (error == -2) {
 			fprintf(stderr, "unfence %s undefined\n", victim);
 			rv = 2;
@@ -241,7 +295,8 @@ int main(int argc, char *argv[])
 			/* Tell fenced what we've done so that it can avoid
 			   fencing this node again if the fence_node() rebooted
 			   it. */
-			fenced_external(victim);
+			if (call_fenced)
+				fenced_external(victim);
 		}
 	}
 
diff --git a/fence/libfence/agent.c b/fence/libfence/agent.c
index 5577fda..fac6c92 100644
--- a/fence/libfence/agent.c
+++ b/fence/libfence/agent.c
@@ -679,3 +679,464 @@ int unfence_node(char *victim, struct fence_log *log, int log_size,
 	return error;
 }
 
+/*
+ * Returns:
+ * < 0: internal error
+ * 0: agent exited with 0
+ * 1: agent exited with 1
+ * 2: agent exited with 2
+ */
+
+static int run_agent_status(char *agent, char *args, int *agent_result)
+{
+	int pid, status, len, rv;
+	int pw_fd = -1;  /* parent write file descriptor */
+	int cr_fd = -1;  /* child read file descriptor */
+	int pfd[2];
+
+	if (args == NULL || agent == NULL) {
+		rv = -1;
+		goto fail;
+	}
+	len = strlen(args);
+
+	if (pipe(pfd)) {
+		rv = -errno;
+		goto fail;
+	}
+	cr_fd = pfd[0];
+	pw_fd = pfd[1];
+
+	pid = fork();
+	if (pid < 0) {
+		rv = -errno;
+		*agent_result = FE_AGENT_FORK;
+		goto fail;
+	}
+
+	if (pid) {
+		/* parent */
+		int ret;
+
+		do {
+			ret = write(pw_fd, args, len);
+		} while (ret < 0 && errno == EINTR);
+
+		if (ret != len) {
+			rv = -1;
+			goto fail;
+		}
+
+		close(cr_fd);
+		close(pw_fd);
+
+		rv = waitpid(pid, &status, 0);
+
+		if (rv < 0) {
+			/* shouldn't happen */
+			rv = -errno;
+			goto out;
+		}
+
+		if (rv != pid) {
+			/* shouldn't happen */
+			rv = -1;
+			goto out;
+		}
+
+		if (WIFEXITED(status)) {
+			/* pid exited properly with an exit code */
+			rv = WEXITSTATUS(status);
+
+			if (rv == 0)
+				*agent_result = FE_AGENT_STATUS_ON;
+			else if (rv == 2)
+				*agent_result = FE_AGENT_STATUS_OFF;
+			else
+				*agent_result = FE_AGENT_STATUS_ERROR;
+		} else if (WIFSIGNALED(status)) {
+			/* pid terminated due to a signal */
+			rv = -1;
+			*agent_result = FE_AGENT_STATUS_ERROR;
+		} else {
+			/* something else happened, not sure what */
+			rv = -1;
+			*agent_result = FE_AGENT_STATUS_ERROR;
+		}
+		goto out;
+
+	} else {
+		/* child */
+		int c_stdout, c_stderr;
+
+		/* redirect agent stdout/stderr to /dev/null */
+		close(1);
+		c_stdout = open("/dev/null", O_WRONLY);
+		if (c_stdout < 0) {
+			rv = -1;
+			goto fail;
+		}
+		close(2);
+		c_stderr = open("/dev/null", O_WRONLY);
+		if (c_stderr < 0) {
+			rv = -1;
+			goto fail;
+		}
+
+		/* redirect agent stdin from parent */
+		close(0);
+		if (dup(cr_fd) < 0) {
+			rv = -errno;
+			goto fail;
+		}
+
+		close(cr_fd);
+		close(pw_fd);
+
+		execlp(agent, agent, NULL);
+		exit(EXIT_FAILURE);
+	}
+ fail:
+	close(cr_fd);
+	close(pw_fd);
+ out:
+	return rv;
+}
+
+static int make_args_status(int cd, char *victim, char *method, int d,
+			    char *device, char **args_out)
+{
+	char path[PATH_MAX];
+	char *args, *str;
+	int error, ret, cnt = 0;
+	size_t len, pos;
+
+	args = malloc(FENCE_AGENT_ARGS_MAX);
+	if (!args)
+		return -ENOMEM;
+	memset(args, 0, FENCE_AGENT_ARGS_MAX);
+
+	len = FENCE_AGENT_ARGS_MAX - 1;
+	pos = 0;
+
+	/* node-specific args for victim */
+
+	memset(path, 0, PATH_MAX);
+	sprintf(path, NODE_FENCE_ARGS_PATH, victim, method, d+1);
+
+	for (;;) {
+		error = ccs_get_list(cd, path, &str);
+		if (error || !str)
+			break;
+		++cnt;
+
+		if (!strncmp(str, "name=", 5)) {
+			free(str);
+			continue;
+		}
+
+		if (!strncmp(str, "action=", 7)) {
+			free(str);
+			continue;
+		}
+
+		ret = snprintf(args + pos, len - pos, "%s\n", str);
+
+		free(str);
+
+		if (ret >= len - pos) {
+			error = -E2BIG;
+			goto out;
+		}
+		pos += ret;
+	}
+
+	/* add nodename of victim to args */
+
+	if (!strstr(args, "nodename=")) {
+		ret = snprintf(args + pos, len - pos, "nodename=%s\n", victim);
+		if (ret >= len - pos) {
+			error = -E2BIG;
+			goto out;
+		}
+		pos += ret;
+	}
+
+	/* add action=status to args */
+
+	ret = snprintf(args + pos, len - pos, "action=status\n");
+	if (ret >= len - pos) {
+		error = -E2BIG;
+		goto out;
+	}
+	pos += ret;
+
+	/* device-specific args */
+
+	memset(path, 0, PATH_MAX);
+	sprintf(path, FENCE_DEVICE_ARGS_PATH, device);
+
+	for (;;) {
+		error = ccs_get_list(cd, path, &str);
+		if (error || !str)
+			break;
+		++cnt;
+
+		if (!strncmp(str, "name=", 5)) {
+			free(str);
+			continue;
+		}
+
+		ret = snprintf(args + pos, len - pos, "%s\n", str);
+
+		free(str);
+
+		if (ret >= len - pos) {
+			error = -E2BIG;
+			goto out;
+		}
+		pos += ret;
+	}
+
+	if (cnt)
+		error = 0;
+ out:
+	if (error) {
+		free(args);
+		args = NULL;
+	}
+
+	*args_out = args;
+	return error;
+}
+
+static int use_device_status(int cd, char *victim, char *method, int d,
+			     char *device, struct fence_log *lp)
+{
+	char path[PATH_MAX], *agent, *args = NULL;
+	int error;
+
+	memset(path, 0, PATH_MAX);
+	sprintf(path, AGENT_NAME_PATH, device);
+
+	error = ccs_get(cd, path, &agent);
+	if (error) {
+		lp->error = FE_READ_AGENT;
+		goto out;
+	}
+
+	strncpy(lp->agent_name, agent, FENCE_AGENT_NAME_MAX-1);
+
+	error = make_args_status(cd, victim, method, d, device, &args);
+	if (error) {
+		lp->error = FE_READ_ARGS;
+		goto out_agent;
+	}
+
+	strncpy(lp->agent_args, args, FENCE_AGENT_ARGS_MAX-1);
+
+	error = run_agent_status(agent, args, &lp->error);
+
+	free(args);
+ out_agent:
+	free(agent);
+ out:
+	return error;
+}
+
+/* We want to run status on each device in each method, and we need all
+   to succeed in order for status as a whole to succeed.  Agent success
+   for status is being either "on" (exit 0) or "off" (exit 2).  Agent
+   failure for status is when the on/off state is unknown (exit 1),
+   i.e. the agent failed to run or ran and cannot connect, or cannot get
+   the on/off state for some reason.
+   
+   As soon as any one device in any method fails, we can quit and report
+   failure (rv < 0) for status as a whole.  If status of all devices is
+   "on", then status as a whole returns 0.  If status of all devices are
+   "off", then status as a whole returns 2.  If the status of all devices
+   are mixed on/off, then status as a whole returns 0. */
+
+int fence_node_status(char *victim, struct fence_log *log, int log_size,
+		      int *log_count, int use_method_num)
+{
+	struct fence_log stub;
+	struct fence_log *lp = log;
+	char *method = NULL, *device = NULL;
+	char *victim_nodename = NULL;
+	int num_methods, num_devices, m, d, cd, rv;
+	int on_count = 0, off_count = 0;
+	int left = log_size;
+	int error = -1;
+	int count = 0;
+
+	cd = ccs_connect();
+	if (cd < 0) {
+		if (lp && left) {
+			lp->error = FE_NO_CONFIG;
+			lp++;
+			left--;
+		}
+		count++;
+		error = -1;
+		goto ret;
+	}
+
+	if (ccs_lookup_nodename(cd, victim, &victim_nodename) == 0)
+		victim = victim_nodename;
+
+	num_methods = count_methods(cd, victim);
+	if (!num_methods) {
+		if (lp && left) {
+			lp->error = FE_NO_METHOD;
+			lp++;
+			left--;
+		}
+		count++;
+		error = -2;		/* No fencing */
+		goto out;
+	}
+
+	if (use_method_num && (use_method_num > num_methods)) {
+		if (lp && left) {
+			lp->error = FE_NUM_METHOD;
+			lp++;
+			left--;
+		}
+		count++;
+		error = -2;		/* No fencing */
+		goto out;
+	}
+
+	for (m = 0; m < num_methods; m++) {
+
+		if (use_method_num && (m + 1 != use_method_num))
+			continue;
+
+		rv = get_method(cd, victim, m, &method);
+		if (rv) {
+			if (lp && left) {
+				lp->error = FE_READ_METHOD;
+				lp->method_num = m;
+				lp++;
+				left--;
+			}
+			count++;
+			error = -1;
+			break;
+		}
+
+		num_devices = count_devices(cd, victim, method);
+		if (!num_devices) {
+			if (lp && left) {
+				lp->error = FE_NO_DEVICE;
+				lp->method_num = m;
+				lp++;
+				left--;
+			}
+			count++;
+			continue;
+		}
+
+		for (d = 0; d < num_devices; d++) {
+			rv = get_device(cd, victim, method, d, &device);
+			if (rv) {
+				if (lp && left) {
+					lp->error = FE_READ_DEVICE;
+					lp->method_num = m;
+					lp->device_num = d;
+					lp++;
+					left--;
+				}
+				count++;
+				error = -1;
+				break;
+			}
+
+			/* every call to use_device generates a log entry,
+			   whether success or fail */
+
+			error = use_device_status(cd, victim, method, d, device,
+						  (lp && left) ? lp : &stub);
+			count++;
+			if (lp && left) {
+				/* error, name, args already set */
+				lp->method_num = m;
+				lp->device_num = d;
+				lp++;
+				left--;
+			}
+
+			/*
+			 * error values:
+			 * < 0: internal error from use_device_status,
+			 *      internal error from run_agent_status,
+			 *      run_agent_status failed to fork agent
+			 * 0: agent exited with 0 (success, status is on)
+			 * 2: agent exited with 2 (success, status is off)
+			 * 1: agent exited with 1 (error, status is unknown)
+			 */
+
+			/* internal error: status fail */
+			if (error < 0)
+				break;
+
+			/* agent error: status fail */
+			if (error == 1) {
+				error = -1;
+				break;
+			}
+
+			if (!error) {
+				/* agent success "on": status success */
+				on_count++;
+			} else if (error == 2) {
+				/* agent success "off": status success */
+				error = 0;
+				off_count++;
+			} else {
+				/* some other error */
+				error = -1;
+				break;
+			}
+
+			free(device);
+			device = NULL;
+		}
+
+		if (device)
+			free(device);
+
+		free(method);
+
+		/* if any device failed in this method, return failure
+		   for the status */
+
+		if (error)
+			break;
+	}
+
+	if (error < 0)
+		goto out;
+
+	/* All devices are either on or off, none are unknown/inaccessible,
+	   so status as a whole is a success.  Decide which of the two
+	   success values to return:  2 if all devices are off, or 0 if
+	   all devices are on, 0 if mixed on/off. */
+
+	if (!on_count)
+		error = 2;
+	else
+		error = 0;
+
+ out:
+	if (victim_nodename)
+		free(victim_nodename);
+
+	ccs_disconnect(cd);
+ ret:
+	if (log_count)
+		*log_count = count;
+	return error;
+}
+
diff --git a/fence/libfence/libfence.h b/fence/libfence/libfence.h
index 33f493a..10d00f0 100644
--- a/fence/libfence/libfence.h
+++ b/fence/libfence/libfence.h
@@ -15,6 +15,10 @@ extern "C" {
 #define FE_READ_ARGS		8	/* read (ccs) error on node/dev args */
 #define FE_READ_METHOD		9	/* read (ccs) error on method */
 #define FE_READ_DEVICE		10	/* read (ccs) error on method/device */
+#define FE_NUM_METHOD		11	/* method number does not exist */
+#define FE_AGENT_STATUS_ON	12
+#define FE_AGENT_STATUS_OFF	13
+#define FE_AGENT_STATUS_ERROR	14
 
 #define FENCE_AGENT_NAME_MAX 256	/* including terminating \0 */
 #define FENCE_AGENT_ARGS_MAX 4096	/* including terminating \0 */
@@ -32,6 +36,28 @@ int fence_node(char *name, struct fence_log *log, int log_size, int *log_count);
 int unfence_node(char *name, struct fence_log *log, int log_size,
 		 int *log_count);
 
+/*
+ * use_method_num == 0: run status on all devices of all methods
+ * use_method_num > 0: run status on all devices of given method number,
+ *                     where first method is use_method_num = 1
+ *
+ * Returns 0 on success: status is successful on all devices of all methods
+ * (or all devices of specified method).  All devices are in the "on" state,
+ * or some devices are on and some are off.
+ *
+ * Returns 2 on success: status is successful on all devices of all methods
+ * (or all devices of a specified method).  All devices are in the "off" state.
+ *
+ * Returns -2 if no fencing methods are defined for the node, or if
+ * use_method_num was specified and the specified method number does
+ * not exist.
+ *
+ * Returns -EXXX for other failures.
+ */
+
+int fence_node_status(char *victim, struct fence_log *log, int log_size,
+                      int *log_count, int use_method_num);
+
 #ifdef __cplusplus
 }
 #endif


More information about the cluster-commits mailing list