Gitweb:
http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=...
Commit: e966498e955358fa16d5f523b09e84e29d6edd80
Parent: 87212596001faaefb9a10ec8f6678a877dc907bb
Author: Christine Caulfield <ccaulfie(a)redhat.com>
AuthorDate: Tue Sep 29 08:59:49 2009 +0100
Committer: Christine Caulfield <ccaulfie(a)redhat.com>
CommitterDate: Tue Sep 29 08:59:49 2009 +0100
cman: Add a validation step to cman_tool join & version subcommands
cman_tool join & cman_tool version now run the configuration through
the schema validator before continuing. By default they will abandon
the operation if they encounter an error. This behaviour can be changed
wit the -D switch to both subcommands.
In addition cman_tool version will also call ccs_sync to distribute the
configuration file if it sees that xmlconfig is being used. This can be
prevented by adding the -S switch.
As it stands, this only really works with xmlconfig because the other
supported configuration (LDAP) needs additional environment variables
to work and these are currently not stored in the corosync objdb. I will
fix this in a future patch.
Signed-off-by: Christine Caulfield <ccaulfie(a)redhat.com>
---
cman/cman_tool/Makefile | 4 +-
cman/cman_tool/cman_tool.h | 9 ++
cman/cman_tool/join.c | 32 +++++++-
cman/cman_tool/main.c | 174 ++++++++++++++++++++++++++++++++++++++------
cman/man/cman_tool.8 | 23 +++++-
5 files changed, 210 insertions(+), 32 deletions(-)
diff --git a/cman/cman_tool/Makefile b/cman/cman_tool/Makefile
index 0dee578..149b758 100644
--- a/cman/cman_tool/Makefile
+++ b/cman/cman_tool/Makefile
@@ -13,12 +13,14 @@ include $(OBJDIR)/make/uninstall.mk
OBJS= main.o \
join.o
-CFLAGS += -DCOROSYNCBIN=\"${corosyncbin}\"
+CFLAGS += -DCOROSYNCBIN=\"${corosyncbin}\" -DSBINDIR=\"${sbindir}\"
CFLAGS += -I${cmanincdir}
CFLAGS += -I${incdir}
+CFLAGS += -I${ccsincdir}
LDFLAGS += -L${cmanlibdir} -lcman
LDFLAGS += -L${libdir}
+LDFLAGS += -L${ccslibdir} -lconfdb -lccs
${TARGET}: ${OBJS}
$(CC) -o $@ $^ $(LDFLAGS)
diff --git a/cman/cman_tool/cman_tool.h b/cman/cman_tool/cman_tool.h
index d6374d5..e84b491 100644
--- a/cman/cman_tool/cman_tool.h
+++ b/cman/cman_tool/cman_tool.h
@@ -57,6 +57,13 @@ enum format_opt
FMT_STATE,
};
+enum validate_options
+{
+ VALIDATE_FAIL = 0,
+ VALIDATE_WARN,
+ VALIDATE_NONE,
+};
+
struct commandline
{
int operation;
@@ -82,6 +89,7 @@ struct commandline
unsigned int config_version;
int config_version_opt;
+ int config_validate_opt;
int votes_opt;
int expected_votes_opt;
int port_opt;
@@ -91,6 +99,7 @@ struct commandline
int wait_quorate_opt;
int addresses_opt;
int noconfig_opt;
+ int nosync_opt;
int nosetpri_opt;
int noopenais_opt;
};
diff --git a/cman/cman_tool/join.c b/cman/cman_tool/join.c
index 02ef73a..697e944 100644
--- a/cman/cman_tool/join.c
+++ b/cman/cman_tool/join.c
@@ -2,6 +2,7 @@
#include <stdint.h>
#include <signal.h>
#include <netinet/in.h>
+#include <corosync/confdb.h>
#include "libcman.h"
#include "cman_tool.h"
@@ -111,10 +112,19 @@ int join(commandline_t *comline, char *main_envp[])
int envptr = 0;
int argvptr = 0;
char scratch[1024];
+ char config_modules[1024];
cman_handle_t h = NULL;
int status;
+ hdb_handle_t object_handle;
+ confdb_handle_t confdb_handle;
+ int res;
pid_t corosync_pid;
int p[2];
+ confdb_callbacks_t callbacks = {
+ .confdb_key_change_notify_fn = NULL,
+ .confdb_object_create_change_notify_fn = NULL,
+ .confdb_object_delete_change_notify_fn = NULL
+ };
/*
* If we can talk to cman then we're already joined (or joining);
@@ -166,15 +176,15 @@ int join(commandline_t *comline, char *main_envp[])
}
if (comline->noconfig_opt) {
envp[envptr++] = strdup("CMAN_NOCONFIG=true");
- snprintf(scratch, sizeof(scratch),
"COROSYNC_DEFAULT_CONFIG_IFACE=cmanpreconfig%s",
+ snprintf(config_modules, sizeof(config_modules), "cmanpreconfig%s",
comline->noopenais_opt?"":":openaisserviceenablestable");
- envp[envptr++] = strdup(scratch);
}
else {
- snprintf(scratch, sizeof(scratch),
"COROSYNC_DEFAULT_CONFIG_IFACE=%s:cmanpreconfig%s", comline->config_lcrso,
+ snprintf(config_modules, sizeof(config_modules), "%s:cmanpreconfig%s",
comline->config_lcrso,
comline->noopenais_opt?"":":openaisserviceenablestable");
- envp[envptr++] = strdup(scratch);
}
+ snprintf(scratch, sizeof(scratch), "COROSYNC_DEFAULT_CONFIG_IFACE=%s",
config_modules);
+ envp[envptr++] = strdup(scratch);
/* Copy any COROSYNC_* env variables to the new daemon */
i=0;
@@ -324,5 +334,19 @@ int join(commandline_t *comline, char *main_envp[])
fprintf(stderr, "corosync started, but not joined the cluster yet.\n");
cman_finish(h);
+
+ /* Save the configuration information in corosync's objdb so we know where we came
from */
+ res = confdb_initialize (&confdb_handle, &callbacks);
+ if (res != CS_OK)
+ goto join_exit;
+
+ res = confdb_object_create(confdb_handle, OBJECT_PARENT_HANDLE,
"cman_private", strlen("cman_private"), &object_handle);
+ if (res == CS_OK) {
+ res = confdb_key_create(confdb_handle, object_handle, "config_modules",
strlen("config_modules"), config_modules, strlen(config_modules));
+
+ }
+ confdb_finalize (confdb_handle);
+
+join_exit:
return 0;
}
diff --git a/cman/cman_tool/main.c b/cman/cman_tool/main.c
index 2d28bc2..d5cfe17 100644
--- a/cman/cman_tool/main.c
+++ b/cman/cman_tool/main.c
@@ -2,6 +2,7 @@
#include <unistd.h>
#include <signal.h>
#include <time.h>
+#include <ccs.h>
#include <netinet/in.h>
#include "copyright.cf"
#include "libcman.h"
@@ -9,7 +10,7 @@
#define DEFAULT_CONFIG_MODULE "xmlconfig"
-#define OPTION_STRING ("m:n:v:e:2p:c:r:i:N:t:o:k:F:C:VAPwfqah?Xd::")
+#define OPTION_STRING ("m:n:v:e:2p:c:r:i:N:t:o:k:F:C:VAPwfqah?XD::Sd::")
#define OP_JOIN 1
#define OP_LEAVE 2
#define OP_EXPECTED 3
@@ -22,7 +23,6 @@
#define OP_SERVICES 10
#define OP_DEBUG 11
-
static void print_usage(int subcmd)
{
printf("Usage:\n");
@@ -57,6 +57,9 @@ static void print_usage(int subcmd)
printf(" -P Don't set corosync to realtime priority\n");
printf(" -X Use internal cman defaults for configuration\n");
printf(" -A Don't load openais services\n");
+ printf(" -D <fail,warn,none> What to do about the config. Default (without
-D) is to validate\n");
+ printf(" the config. with -D no validation will be done. -Dwarn
will print errors\n");
+ printf(" but allow the operation to continue\n");
printf("\n");
}
@@ -116,6 +119,10 @@ static void print_usage(int subcmd)
if (!subcmd || subcmd == OP_VERSION) {
printf("version\n");
printf(" -r <config> A new config version to set on all
members\n");
+ printf(" -D <fail,warn,none> What to do about the config. Default (without
-D) is to validate\n");
+ printf(" the config. with -D no validation will be done. -Dwarn
will print errors\n");
+ printf(" but allow the operation to continue\n");
+ printf(" -S Don't run ccs_sync to distribute cluster.conf (if
appropriate)\n");
printf("\n");
}
}
@@ -642,11 +649,62 @@ static void set_votes(commandline_t *comline)
cman_finish(h);
}
+static int validate_config(commandline_t *comline, char *config_value)
+{
+ struct stat st;
+ char command[PATH_MAX];
+ char validator[PATH_MAX];
+ int cmd_res;
+
+ /* Look for ccs_config_validate */
+ snprintf(validator, sizeof(validator), "%s/ccs_config_validate", SBINDIR);
+ if (stat(validator, &st) != 0 || !(st.st_mode & S_IXUSR)) {
+ fprintf(stderr, "Cannot find ccs_config_validate, configuration was not checked
but assumed to be OK.\n");
+ return 0;
+ }
+
+ snprintf(command, sizeof(command), "%s -C %s", validator, config_value);
+
+ if (comline->verbose > 1)
+ printf("calling '%s'\n", command);
+
+ cmd_res = system(command);
+
+ return cmd_res;
+}
+
+
+static int get_config_variable(commandline_t *comline, char **config_modules)
+{
+ int ccs_handle;
+ int ccs_res;
+ char *config_value;
+
+ /* Get the config modules that corosync was loaded with */
+ ccs_handle = ccs_connect();
+ if (!ccs_handle) {
+ fprintf(stderr, "Cannot contact ccs to get configuration information\n");
+ return 1;
+ }
+
+ ccs_res = ccs_get(ccs_handle, "/cman_private/@config_modules",
&config_value);
+ ccs_disconnect(ccs_handle);
+ if (ccs_res) {
+ fprintf(stderr, "Cannot work out where corosync got it's configuration
information from so I\n");
+ fprintf(stderr, "can't validate it. Use the -D switch to bypass this and load
the\n");
+ fprintf(stderr, "configuration unchecked.\n");
+ return 1;
+ }
+ *config_modules = config_value;
+ return 0;
+}
+
static void version(commandline_t *comline)
{
struct cman_version ver;
cman_handle_t h;
int result;
+ char *config_modules = NULL;
h = open_cman_handle(1);
@@ -659,10 +717,35 @@ static void version(commandline_t *comline)
goto out;
}
- ver.cv_config = comline->config_version;
+ if (comline->verbose)
+ printf("Getting config variables\n");
+ if (get_config_variable(comline, &config_modules))
+ die("");
- if ((result = cman_set_version(h, &ver)))
- die("can't set version: %s", cman_error(errno));
+ /* By default we validate the configuration first */
+ if (comline->config_validate_opt != VALIDATE_NONE) {
+
+ if (comline->verbose)
+ printf("Validating configuration\n");
+ if (validate_config(comline, config_modules) &&
+ comline->config_validate_opt == VALIDATE_FAIL)
+ die("Not reloading, configuration is not valid\n");
+ }
+
+ if (strstr(config_modules, "xmlconfig") && !comline->nosync_opt) {
+ if (comline->verbose > 1)
+ printf("calling ccs_sync\n");
+ result = system("/usr/bin/ccs_sync");
+ }
+ else {
+ if (comline->verbose)
+ printf("Telling cman the new version number\n");
+
+ ver.cv_config = comline->config_version;
+
+ if ((result = cman_set_version(h, &ver)))
+ die("can't set version: %s", cman_error(errno));
+ }
out:
cman_finish(h);
}
@@ -752,6 +835,7 @@ static void decode_arguments(int argc, char *argv[], commandline_t
*comline)
{
int cont = TRUE;
int optchar, i;
+ int suboptchar;
int show_help = 0;
while (cont) {
@@ -767,6 +851,34 @@ static void decode_arguments(int argc, char *argv[], commandline_t
*comline)
comline->addresses_opt = 1;
break;
+ case 'D':
+ /* Just look at the upper-cased version of the first letter of the argument */
+ if (optarg) {
+ suboptchar = optarg[0] & 0x5F;
+ switch (suboptchar)
+ {
+ case 'F':
+ comline->config_validate_opt = VALIDATE_FAIL;
+ break;
+ case 'W':
+ comline->config_validate_opt = VALIDATE_WARN;
+ break;
+ case 'N':
+ comline->config_validate_opt = VALIDATE_NONE;
+ break;
+ default:
+ die("invalid option to -D, it should be 'force', 'warn' or
'none'\n");
+ break;
+ }
+ }
+ else {
+ comline->config_validate_opt = VALIDATE_NONE;
+ }
+ break;
+ case 'S':
+ comline->nosync_opt = 1;
+ break;
+
case 'n':
i = comline->num_nodenames;
if (i >= MAX_INTERFACES)
@@ -981,10 +1093,43 @@ static void check_arguments(commandline_t *comline)
die("timeout is only appropriate with wait");
}
+
+static void do_join(commandline_t *comline, char *envp[])
+{
+ int ret;
+
+ check_arguments(comline);
+
+ if (comline->timeout) {
+ signal(SIGALRM, sigalarm_handler);
+ alarm(comline->timeout);
+ }
+
+ /* By default we validate the configuration first */
+ if (comline->config_validate_opt != VALIDATE_NONE) {
+
+ if (comline->verbose)
+ printf("Validating configuration\n");
+
+ if (validate_config(comline, comline->config_lcrso) &&
+ comline->config_validate_opt == VALIDATE_FAIL)
+ die("Not reloading, configuration is not valid\n");
+ }
+
+ join(comline, envp);
+ if (comline->wait_opt || comline->wait_quorate_opt) {
+ do {
+ ret = cluster_wait(comline);
+ if (ret == ENOTCONN)
+ join(comline, envp);
+
+ } while (ret == ENOTCONN);
+ }
+}
+
int main(int argc, char *argv[], char *envp[])
{
commandline_t comline;
- int ret;
prog_name = argv[0];
@@ -994,22 +1139,7 @@ int main(int argc, char *argv[], char *envp[])
switch (comline.operation) {
case OP_JOIN:
- check_arguments(&comline);
-
- if (comline.timeout) {
- signal(SIGALRM, sigalarm_handler);
- alarm(comline.timeout);
- }
-
- join(&comline, envp);
- if (comline.wait_opt || comline.wait_quorate_opt) {
- do {
- ret = cluster_wait(&comline);
- if (ret == ENOTCONN)
- join(&comline, envp);
-
- } while (ret == ENOTCONN);
- }
+ do_join(&comline, envp);
break;
case OP_LEAVE:
diff --git a/cman/man/cman_tool.8 b/cman/man/cman_tool.8
index 8d45048..4d400a6 100644
--- a/cman/man/cman_tool.8
+++ b/cman/man/cman_tool.8
@@ -69,12 +69,11 @@ with -r to tell cluster members to update.
.br
The argument to -r is the version number that cman should look for. If
that version is not currently available then cman will poll for it. If
-a version of 0 is specified then cman will simply re-read the configuration
-and use the version number it finds there.
+a version of 0 is specified then cman will read the configuration file,
+validate it, distribute it around the cluster (if necessary) and
+activate it.
.br
-NOTE: this happens cluster-wide and the highest version number in the
-cluster will be the one everyone looks for. version -r0 is not a way
-to have different config versions across nodes in the cluster!
+The -D flag can disable the validation stage. This is NOT recommended.
.TP
.I wait
@@ -151,6 +150,9 @@ cman_tool version on its own will always show the current version
and not the one being looked for. So be aware that the display
will possible not update immediately after you have run
cman_tool version -r.
+.TP
+.I -D<option>
+see "JOIN" options
.SH "WAIT" OPTIONS
.TP
.I -q
@@ -290,6 +292,17 @@ Don't load openais services. Normally cman_tool join will load
the configuration
module 'openaisserviceenablestable' which will load the services installed by
openais.
If you don't want to use these services or have not installed openais then
this switch will disable them.
+.TP
+.I -D
+Tells cman_tool whether to validate the configuration before loading or reloading it.
+By default the configuration
+.B is
+validated, which is equivalent to -Dfail.
+.br
+-Dwarn will validate the configuration and print any messages arising, but will attempt
+to use it regardless of its validity.
+.br
+-Dnone (or just -D) will skip the validation completely.
.SH "NODES" OPTIONS
.TP
.I -a