Hello community,
here is the log from the commit of package booth for openSUSE:Factory checked in at 2015-11-28 15:19:04
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/booth (Old)
and /work/SRC/openSUSE:Factory/.booth.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "booth"
Changes:
--------
--- /work/SRC/openSUSE:Factory/booth/booth.changes 2015-11-08 11:26:58.000000000 +0100
+++ /work/SRC/openSUSE:Factory/.booth.new/booth.changes 2015-11-28 15:19:06.000000000 +0100
@@ -1,0 +2,14 @@
+Tue Nov 24 13:11:00 UTC 2015 - dmuhamedagic@suse.com
+
+- Update to version v0.2.0_120_gf3d73a5:
+ + arbitrator: mark expired tickets as lost (bsc#956321)
+ + attr: better control of election cause
+
+-------------------------------------------------------------------
+Fri Nov 20 11:37:43 UTC 2015 - dmuhamedagic@suse.com
+
+- Update to version v0.2.0_116_g88c3d6a:
+ + attr: attribute prerequisites (fate#318182)
+ + attr: keep attributes in the CIB (fate#318182)
+
+-------------------------------------------------------------------
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.amwQe7/_old 2015-11-28 15:19:07.000000000 +0100
+++ /var/tmp/diff_new_pack.amwQe7/_new 2015-11-28 15:19:07.000000000 +0100
@@ -1,4 +1,4 @@
<servicedata>
<service name="tar_scm">
<param name="url">git://github.com/ClusterLabs/booth.git</param>
- <param name="changesrevision">3e73b683b36c38650d1d2ff4c9393cb1bb2056e6</param></service></servicedata>
\ No newline at end of file
+ <param name="changesrevision">88c3d6a865cea676778bdeb7858f715a6ece6b10</param></service></servicedata>
\ No newline at end of file
++++++ booth.tar.bz2 ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth/.git_info new/booth/.git_info
--- old/booth/.git_info 2015-11-06 10:07:54.000000000 +0100
+++ new/booth/.git_info 2015-11-24 14:08:30.000000000 +0100
@@ -1 +1 @@
-v0.2.0-113-gaeef08d
+v0.2.0-120-gf3d73a5
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth/configure.ac new/booth/configure.ac
--- old/booth/configure.ac 2015-11-06 09:20:07.000000000 +0100
+++ new/booth/configure.ac 2015-11-23 19:13:11.000000000 +0100
@@ -61,6 +61,7 @@
AC_PATH_PROGS(PKGCONFIG, pkg-config)
AC_PATH_PROGS(ASCIIDOC, asciidoc)
+AC_PATH_PROGS(XML2CONFIG, xml2-config)
AM_CONDITIONAL(BUILD_ASCIIDOC, test x"${ASCIIDOC}" != x"")
@@ -82,6 +83,17 @@
AM_CONDITIONAL(BUILD_AUTH_C, test "x${mhash_installed}" = "xyes")
fi
+AC_MSG_CHECKING(for special libxml2 includes)
+if test "x$XML2CONFIG" = "x"; then
+ AC_MSG_ERROR(libxml2 config not found)
+else
+ XML2HEAD="`$XML2CONFIG --cflags`"
+ AC_MSG_RESULT($XML2HEAD)
+ AC_CHECK_LIB(xml2, xmlReadMemory)
+fi
+
+CPPFLAGS="$CPPFLAGS $XML2HEAD"
+
PKG_CHECK_MODULES(GLIB, [glib-2.0])
# Checks for header files.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth/docs/boothd.8.txt new/booth/docs/boothd.8.txt
--- old/booth/docs/boothd.8.txt 2015-11-06 09:20:07.000000000 +0100
+++ new/booth/docs/boothd.8.txt 2015-11-23 19:13:11.000000000 +0100
@@ -325,6 +325,38 @@
The distributed 'service-runnable' script is an example which may
be used to test whether a pacemaker resource can be started.
+*'attr-prereq'*::
+ Sites can have GEO attributes managed with the 'geostore(8)'
+ program. Attributes are within ticket's scope and may be
+ tested by 'boothd' for additional control of ticket failover
+ (automatic) or ticket acquire (manual).
++
+Attributes are typically used to convey extra information about
+resources, for instance database replication status. The
+attributes are commonly updated by resource agents.
++
+Attribute values are referenced in expressions and may be tested
+for equality with the 'eq' binary operator or inequality with the
+'ne' operator. The usage is as follows:
+
+ attr-prereq = <name> <op> <value>
+
+ : "auto" | "manual"
+ <name>: attribute name
+ <op>: "eq" | "ne"
+ <value>: attribute value
++
+The two grant types are 'auto' for ticket failover and 'manual'
+for grants using the booth client. Only in case the expression
+evaluates to true can the ticket be granted.
++
+It is not clear whether the 'manual' grant type has any practical
+use because, obviously, this operation is anyway controlled by a
+human.
++
+Note that there can be no guarantee on whether an attribute value
+is up to date, i.e. if it actually reflects the current state.
+
One example of a booth configuration file:
-----------------------
@@ -345,6 +377,7 @@
retries = 5
renewal-freq = 60
before-acquire-handler = /usr/share/booth/service-runnable db8
+ attr-prereq = auto repl_state eq ACTIVE
-----------------------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth/src/attr.c new/booth/src/attr.c
--- old/booth/src/attr.c 2015-11-06 09:20:07.000000000 +0100
+++ new/booth/src/attr.c 2015-11-23 19:13:11.000000000 +0100
@@ -19,6 +19,7 @@
#include
#include "attr.h"
#include "ticket.h"
+#include "pacemaker.h"
void print_geostore_usage(void)
{
@@ -243,11 +244,13 @@
g_free(a);
}
-static cmd_result_t attr_set(struct ticket_config *tk, struct boothc_attr_msg *msg)
+int store_geo_attr(struct ticket_config *tk, const char *name, char *val, int notime)
{
struct geo_attr *a;
GDestroyNotify free_geo_attr_notify = free_geo_attr;
+ if (!tk)
+ return -1;
/*
* allocate new, if attr doesn't already exist
* copy the attribute value
@@ -258,21 +261,34 @@
free_geo_attr_notify, g_free);
if (!tk->attr) {
log_error("out of memory");
- return RLT_SYNC_FAIL;
+ return -1;
}
a = (struct geo_attr *)calloc(1, sizeof(struct geo_attr));
if (!a) {
log_error("out of memory");
- return RLT_SYNC_FAIL;
+ return -1;
}
- a->val = g_strdup(msg->attr.val);
- get_time(&a->update_ts);
+ a->val = g_strdup(val);
+ if (!notime)
+ get_time(&a->update_ts);
g_hash_table_insert(tk->attr,
- g_strndup(msg->attr.name, BOOTH_NAME_LEN), a);
+ g_strndup(name, BOOTH_NAME_LEN), a);
+
+ return 0;
+}
+
+static cmd_result_t attr_set(struct ticket_config *tk, struct boothc_attr_msg *msg)
+{
+ int rc;
+ rc = store_geo_attr(tk, msg->attr.name, msg->attr.val, 0);
+ if (rc) {
+ return RLT_SYNC_FAIL;
+ }
+ (void)pcmk_handler.set_attr(tk, msg->attr.name, msg->attr.val);
return RLT_SUCCESS;
}
@@ -296,6 +312,8 @@
rv = g_hash_table_remove(tk->attr, msg->attr.name);
+ (void)pcmk_handler.del_attr(tk, msg->attr.name);
+
return gbool2rlt(rv);
}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth/src/attr.h new/booth/src/attr.h
--- old/booth/src/attr.h 2015-11-06 09:20:07.000000000 +0100
+++ new/booth/src/attr.h 2015-11-23 19:13:11.000000000 +0100
@@ -34,5 +34,6 @@
int do_attr_command(cmd_request_t cmd);
int process_attr_request(struct client *req_client, void *buf);
int attr_recv(void *buf, struct booth_site *source);
+int store_geo_attr(struct ticket_config *tk, const char *name, char *val, int notime);
#endif /* _ATTR_H */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth/src/booth.h new/booth/src/booth.h
--- old/booth/src/booth.h 2015-11-06 09:20:07.000000000 +0100
+++ new/booth/src/booth.h 2015-11-23 19:13:11.000000000 +0100
@@ -244,6 +244,7 @@
RLT_NO_SUCH_ATTR = CHAR2CONST('N', 'A', 't', 'r'),
RLT_CIB_PENDING = CHAR2CONST('P', 'e', 'n', 'd'),
RLT_EXT_FAILED = CHAR2CONST('X', 'P', 'r', 'g'),
+ RLT_ATTR_PREREQ = CHAR2CONST('A', 'P', 'r', 'q'),
RLT_TICKET_IDLE = CHAR2CONST('T', 'i', 'd', 'l'),
RLT_OVERGRANT = CHAR2CONST('O', 'v', 'e', 'r'),
RLT_PROBABLY_SUCCESS = CHAR2CONST('S', 'u', 'c', '?'),
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth/src/config.c new/booth/src/config.c
--- old/booth/src/config.c 2015-11-06 09:20:07.000000000 +0100
+++ new/booth/src/config.c 2015-11-23 19:13:11.000000000 +0100
@@ -370,6 +370,103 @@
return 0;
}
+struct toktab grant_type[] = {
+ { "auto", GRANT_AUTO},
+ { "manual", GRANT_MANUAL},
+ { NULL, 0},
+};
+
+struct toktab attr_op[] = {
+ {"eq", ATTR_OP_EQ},
+ {"ne", ATTR_OP_NE},
+ {NULL, 0},
+};
+
+static int lookup_tokval(char *key, struct toktab *tab)
+{
+ struct toktab *tp;
+
+ for (tp = tab; tp->str; tp++) {
+ if (!strcmp(tp->str, key))
+ return tp->val;
+ }
+ return 0;
+}
+
+/* attribute prerequisite
+ */
+static int parse_attr_prereq(char *val, struct ticket_config *tk)
+{
+ struct attr_prereq *ap = NULL;
+ char *p;
+
+ ap = (struct attr_prereq *)calloc(1, sizeof(struct attr_prereq));
+ if (!ap) {
+ log_error("out of memory");
+ return -1;
+ }
+
+ p = strtok(val, " \t");
+ if (!p) {
+ log_error("not enough arguments to attr-prereq");
+ goto err_out;
+ }
+ ap->grant_type = lookup_tokval(p, grant_type);
+ if (!ap->grant_type) {
+ log_error("%s is not a grant type", p);
+ goto err_out;
+ }
+
+ p = strtok(NULL, " \t");
+ if (!p) {
+ log_error("not enough arguments to attr-prereq");
+ goto err_out;
+ }
+ if (!(ap->attr_name = strdup(p))) {
+ log_error("out of memory");
+ goto err_out;
+ }
+
+ p = strtok(NULL, " \t");
+ if (!p) {
+ log_error("not enough arguments to attr-prereq");
+ goto err_out;
+ }
+ ap->op = lookup_tokval(p, attr_op);
+ if (!ap->op) {
+ log_error("%s is not an attribute operation", p);
+ goto err_out;
+ }
+
+ p = strtok(NULL, " \t");
+ if (!p) {
+ log_error("not enough arguments to attr-prereq");
+ goto err_out;
+ }
+ if (!(ap->attr_val = strdup(p))) {
+ log_error("out of memory");
+ goto err_out;
+ }
+
+ tk->attr_prereqs = g_list_append(tk->attr_prereqs, ap);
+ if (!tk->attr_prereqs) {
+ log_error("out of memory");
+ goto err_out;
+ }
+
+ return 0;
+
+err_out:
+ if (ap) {
+ if (ap->attr_val)
+ free(ap->attr_val);
+ if (ap->attr_name)
+ free(ap->attr_name);
+ free(ap);
+ }
+ return -1;
+}
+
extern int poll_timeout;
int read_config(const char *path, int type)
@@ -680,6 +777,13 @@
goto err;
}
continue;
+ }
+
+ if (strcmp(key, "attr-prereq") == 0) {
+ if (parse_attr_prereq(val, current_tk)) {
+ goto err;
+ }
+ continue;
}
if (strcmp(key, "weights") == 0) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth/src/config.h new/booth/src/config.h
--- old/booth/src/config.h 2015-11-06 09:20:07.000000000 +0100
+++ new/booth/src/config.h 2015-11-23 19:13:11.000000000 +0100
@@ -46,6 +46,28 @@
#define tk_test tk->clu_test
+typedef enum {
+ ATTR_OP_EQ = 1,
+ ATTR_OP_NE,
+} attr_op_e;
+
+typedef enum {
+ GRANT_AUTO = 1,
+ GRANT_MANUAL,
+} grant_type_e;
+
+struct toktab {
+ const char *str;
+ int val;
+};
+
+struct attr_prereq {
+ grant_type_e grant_type; /* grant type */
+ attr_op_e op; /* attribute operation */
+ char *attr_name;
+ char *attr_val;
+};
+
struct ticket_config {
/** \name Configuration items.
* @{ */
@@ -200,6 +222,10 @@
*/
GHashTable *attr;
+ /** Attribute prerequisites
+ */
+ GList *attr_prereqs;
+
/** Whom to vote for the next time.
* Needed to push a ticket to someone else. */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth/src/main.c new/booth/src/main.c
--- old/booth/src/main.c 2015-11-06 09:20:07.000000000 +0100
+++ new/booth/src/main.c 2015-11-23 19:13:11.000000000 +0100
@@ -599,6 +599,12 @@
rv = -1;
break;
+ case RLT_ATTR_PREREQ:
+ log_error("attr-prereq for ticket \"%s\" failed, grant denied",
+ cl.msg.ticket.id);
+ rv = -1;
+ break;
+
case RLT_REDIRECT:
/* talk to another site */
rv = 1;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth/src/pacemaker.c new/booth/src/pacemaker.c
--- old/booth/src/pacemaker.c 2015-11-06 09:20:07.000000000 +0100
+++ new/booth/src/pacemaker.c 2015-11-23 19:13:11.000000000 +0100
@@ -21,9 +21,14 @@
#include
#include
#include
+#include
#include
#include
+#include
+#include
+#include "ticket.h"
#include "log.h"
+#include "attr.h"
#include "pacemaker.h"
#include "inline-fn.h"
@@ -196,15 +201,10 @@
}
-static int crm_ticket_set(const struct ticket_config *tk, const char *attr, int64_t val)
+static int _run_crm_ticket(char *cmd)
{
- char cmd[COMMAND_MAX];
int i, rv;
-
- snprintf(cmd, COMMAND_MAX,
- "crm_ticket -t '%s' -S '%s' -v %" PRIi64,
- tk->name, attr, val);
/* If there are errors, there's not much we can do but retry ... */
for (i=0; i<3 &&
(rv = system(cmd));
@@ -215,6 +215,36 @@
return rv;
}
+static int crm_ticket_set_int(const struct ticket_config *tk, const char *attr, int64_t val)
+{
+ char cmd[COMMAND_MAX];
+
+ snprintf(cmd, COMMAND_MAX,
+ "crm_ticket -t '%s' -S '%s' -v %" PRIi64,
+ tk->name, attr, val);
+ return _run_crm_ticket(cmd);
+}
+
+static int pcmk_set_attr(struct ticket_config *tk, const char *attr, const char *val)
+{
+ char cmd[COMMAND_MAX];
+
+ snprintf(cmd, COMMAND_MAX,
+ "crm_ticket -t '%s' -S '%s' -v '%s'",
+ tk->name, attr, val);
+ return _run_crm_ticket(cmd);
+}
+
+static int pcmk_del_attr(struct ticket_config *tk, const char *attr)
+{
+ char cmd[COMMAND_MAX];
+
+ snprintf(cmd, COMMAND_MAX,
+ "crm_ticket -t '%s' -D '%s'",
+ tk->name, attr);
+ return _run_crm_ticket(cmd);
+}
+
static int pcmk_store_ticket_nonatomic(struct ticket_config *tk)
{
@@ -222,9 +252,9 @@
/* Always try to store *each* attribute, even if there's an error
* for one of them. */
- rv = crm_ticket_set(tk, "owner", (int32_t)get_node_id(tk->leader));
- rv = crm_ticket_set(tk, "expires", wall_ts(&tk->term_expires)) || rv;
- rv = crm_ticket_set(tk, "term", tk->current_term) || rv;
+ rv = crm_ticket_set_int(tk, "owner", (int32_t)get_node_id(tk->leader));
+ rv = crm_ticket_set_int(tk, "expires", wall_ts(&tk->term_expires)) || rv;
+ rv = crm_ticket_set_int(tk, "term", tk->current_term) || rv;
if (rv)
log_error("setting crm_ticket attributes failed; %s",
@@ -235,19 +265,88 @@
return rv;
}
+typedef int (*attr_f)(struct ticket_config *tk, const char *name, char *val);
+
+struct attr_tab
+{
+ const char *name;
+ attr_f handling_f;
+};
-static int crm_ticket_get(struct ticket_config *tk,
- const char *attr, int64_t *data)
+static int save_expires(struct ticket_config *tk, const char *name, char *val)
+{
+ secs2tv(unwall_ts(atol(val)), &tk->term_expires);
+ return 0;
+}
+
+static int save_term(struct ticket_config *tk, const char *name, char *val)
+{
+ tk->current_term = atol(val);
+ return 0;
+}
+
+static int parse_boolean(char *val)
+{
+ long v;
+
+ if (!strncmp(val, "false", 5)) {
+ v = 0;
+ } else if (!strncmp(val, "true", 4)) {
+ v = 1;
+ } else {
+ v = atol(val);
+ }
+ return v;
+}
+
+static int save_granted(struct ticket_config *tk, const char *name, char *val)
+{
+ tk->is_granted = parse_boolean(val);
+ return 0;
+}
+
+static int save_owner(struct ticket_config *tk, const char *name, char *val)
+{
+ /* No check, node could have been deconfigured. */
+ tk->leader = NULL;
+ return !find_site_by_id(atol(val), &tk->leader);
+}
+
+static int ignore_attr(struct ticket_config *tk, const char *name, char *val)
+{
+ return 0;
+}
+
+static int save_attr(struct ticket_config *tk, const char *name, char *val)
+{
+ /* tell store_geo_attr not to store time, we don't have that
+ * information available
+ */
+ return store_geo_attr(tk, name, val, 1);
+}
+
+struct attr_tab attr_handlers[] = {
+ { "expires", save_expires},
+ { "term", save_term},
+ { "granted", save_granted},
+ { "owner", save_owner},
+ { "id", ignore_attr},
+ { "last-granted", ignore_attr},
+ { NULL, 0},
+};
+
+
+/* get_attr is currently not used and has not been tested
+ */
+static int pcmk_get_attr(struct ticket_config *tk, const char *attr, const char **vp)
{
char cmd[COMMAND_MAX];
- char line[256];
- int rv;
- int64_t v;
+ char line[BOOTH_ATTRVAL_LEN+1];
+ int rv = 0;
FILE *p;
- *data = -1;
- v = 0;
+ *vp = NULL;
snprintf(cmd, COMMAND_MAX,
"crm_ticket -t '%s' -G '%s' --quiet",
tk->name, attr);
@@ -257,85 +356,167 @@
rv = errno;
log_error("popen error %d (%s) for \"%s\"",
rv, strerror(rv), cmd);
- return rv || -EINVAL;
+ return rv || EINVAL;
}
- if (fgets(line, sizeof(line) - 1, p) == NULL) {
+ if (fgets(line, BOOTH_ATTRVAL_LEN, p) == NULL) {
rv = ENODATA;
goto out;
}
- rv = EINVAL;
- if (!strncmp(line, "false", 5)) {
- v = 0;
- rv = 0;
- } else if (!strncmp(line, "true", 4)) {
- v = 1;
- rv = 0;
- } else if (sscanf(line, "%" PRIi64, &v) == 1) {
- rv = 0;
- }
-
- *data = v;
+ *vp = g_strdup(line);
out:
rv = pclose(p);
if (!rv) {
- log_debug("command \"%s\" value %" PRIi64, cmd, v);
+ log_debug("command \"%s\"", cmd);
} else if (WEXITSTATUS(rv) == 6) {
log_info("command \"%s\", ticket not found", cmd);
} else {
- log_error("command \"%s\" %s, value %" PRIi64, cmd, interpret_rv(rv), v);
+ log_error("command \"%s\" %s", cmd, interpret_rv(rv));
+ }
+ return rv;
+}
+
+static int save_attributes(struct ticket_config *tk, xmlDocPtr doc)
+{
+ int rv = 0, rc;
+ xmlNodePtr n;
+ xmlAttrPtr attr;
+ xmlChar *v;
+ struct attr_tab *atp;
+
+ n = xmlDocGetRootElement(doc);
+ if (n == NULL) {
+ tk_log_error("crm_ticket xml output empty");
+ return -EINVAL;
+ }
+ if (xmlStrcmp(n->name, (const xmlChar *)"ticket_state")) {
+ tk_log_error("crm_ticket xml root element not ticket_state");
+ return -EINVAL;
+ }
+ for (attr = n->properties; attr; attr = attr->next) {
+ v = xmlGetProp(n, attr->name);
+ for (atp = attr_handlers; atp->name; atp++) {
+ if (!strcmp(atp->name, attr->name)) {
+ rc = atp->handling_f(tk, attr->name, v);
+ break;
+ }
+ }
+ if (!atp->name) {
+ rc = save_attr(tk, attr->name, v);
+ }
+ if (rc) {
+ tk_log_error("error storing attribute %s", attr->name);
+ rv |= rc;
+ }
+ xmlFree(v);
}
return rv;
}
-static int pcmk_load_ticket(struct ticket_config *tk)
+#define CHUNK_SIZE 256
+
+static int parse_ticket_state(struct ticket_config *tk, FILE *p)
{
- int rv;
- int64_t v;
+ int rv = 0;
+ GString *input = NULL;
+ char line[CHUNK_SIZE];
+ xmlDocPtr doc = NULL;
+ xmlErrorPtr errptr;
+ int opts = XML_PARSE_COMPACT | XML_PARSE_NONET;
+
+ /* skip first two lines of output */
+ if (fgets(line, CHUNK_SIZE-1, p) == NULL || fgets(line, CHUNK_SIZE-1, p) == NULL) {
+ tk_log_error("crm_ticket xml output empty");
+ rv = ENODATA;
+ goto out;
+ }
+ input = g_string_sized_new(CHUNK_SIZE);
+ if (!input) {
+ log_error("out of memory");
+ rv = -1;
+ goto out;
+ }
+ while (fgets(line, CHUNK_SIZE-1, p) != NULL) {
+ if (!g_string_append(input, line)) {
+ log_error("out of memory");
+ rv = -1;
+ goto out;
+ }
+ }
+
+ doc = xmlReadDoc(input->str, NULL, NULL, opts);
+ if (doc == NULL) {
+ errptr = xmlGetLastError();
+ if (errptr) {
+ tk_log_error("crm_ticket xml parse failed (domain=%d, level=%d, code=%d): %s",
+ errptr->domain, errptr->level,
+ errptr->code, errptr->message);
+ } else {
+ tk_log_error("crm_ticket xml parse failed");
+ }
+ rv = -EINVAL;
+ goto out;
+ }
+ rv = save_attributes(tk, doc);
+out:
+ if (doc)
+ xmlFreeDoc(doc);
+ if (input)
+ g_string_free(input, TRUE);
+ return rv;
+}
+
+static int pcmk_load_ticket(struct ticket_config *tk)
+{
+ char cmd[COMMAND_MAX];
+ int rv = 0, pipe_rv;
+ FILE *p;
/* This here gets run during startup; testing that here means that
* normal operation won't be interrupted with that test. */
test_atomicity();
+ snprintf(cmd, COMMAND_MAX,
+ "crm_ticket -t '%s' -q",
+ tk->name);
- rv = crm_ticket_get(tk, "expires", &v);
- if (!rv) {
- secs2tv(unwall_ts(v), &tk->term_expires);
- }
-
- rv = crm_ticket_get(tk, "term", &v);
- if (!rv) {
- tk->current_term = v;
+ p = popen(cmd, "r");
+ if (p == NULL) {
+ pipe_rv = errno;
+ log_error("popen error %d (%s) for \"%s\"",
+ pipe_rv, strerror(pipe_rv), cmd);
+ return pipe_rv || -EINVAL;
}
- rv = crm_ticket_get(tk, "granted", &v);
- if (!rv) {
- tk->is_granted = v;
- }
+ rv = parse_ticket_state(tk, p);
- rv = crm_ticket_get(tk, "owner", &v);
- if (!rv) {
- /* No check, node could have been deconfigured. */
- if (!find_site_by_id(v, &tk->leader)) {
- /* Hmm, no site found for the ticket we have in the
- * CIB!?
- * Assume that the ticket belonged to us if it was
- * granted here!
- */
- log_warn("%s: no site matches; site got reconfigured?",
+ if (!tk->leader) {
+ /* Hmm, no site found for the ticket we have in the
+ * CIB!?
+ * Assume that the ticket belonged to us if it was
+ * granted here!
+ */
+ log_warn("%s: no site matches; site got reconfigured?",
+ tk->name);
+ if (tk->is_granted) {
+ log_warn("%s: granted here, assume it belonged to us",
tk->name);
- if (tk->is_granted) {
- log_warn("%s: granted here, assume it belonged to us",
- tk->name);
- tk->leader = local;
- }
+ tk->leader = local;
}
}
- return rv;
+ pipe_rv = pclose(p);
+ if (!pipe_rv) {
+ log_debug("command \"%s\"", cmd);
+ } else if (WEXITSTATUS(pipe_rv) == 6) {
+ log_info("command \"%s\", ticket not found", cmd);
+ } else {
+ log_error("command \"%s\" %s", cmd, interpret_rv(pipe_rv));
+ }
+ return rv | pipe_rv;
}
@@ -343,4 +524,7 @@
.grant_ticket = pcmk_grant_ticket,
.revoke_ticket = pcmk_revoke_ticket,
.load_ticket = pcmk_load_ticket,
+ .set_attr = pcmk_set_attr,
+ .get_attr = pcmk_get_attr,
+ .del_attr = pcmk_del_attr,
};
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth/src/pacemaker.h new/booth/src/pacemaker.h
--- old/booth/src/pacemaker.h 2015-11-06 09:20:07.000000000 +0100
+++ new/booth/src/pacemaker.h 2015-11-23 19:13:11.000000000 +0100
@@ -27,6 +27,9 @@
int (*grant_ticket) (struct ticket_config *tk);
int (*revoke_ticket) (struct ticket_config *tk);
int (*load_ticket) (struct ticket_config *tk);
+ int (*set_attr) (struct ticket_config *tk, const char *a, const char *v);
+ int (*get_attr) (struct ticket_config *tk, const char *a, const char **vp);
+ int (*del_attr) (struct ticket_config *tk, const char *a);
};
struct ticket_handler pcmk_handler;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth/src/raft.c new/booth/src/raft.c
--- old/booth/src/raft.c 2015-11-06 09:20:07.000000000 +0100
+++ new/booth/src/raft.c 2015-11-23 19:13:11.000000000 +0100
@@ -726,6 +726,8 @@
return booth_udp_send_auth(sender, &omsg, sendmsglen(&omsg));
}
+#define is_reason(r, tk) \
+ (reason == (r) || (reason == OR_AGAIN && (tk)->election_reason == (r)))
int new_election(struct ticket_config *tk,
struct booth_site *preference, int update_term, cmd_reason_t reason)
@@ -735,6 +737,13 @@
if (local->type != SITE)
return 0;
+ if ((is_reason(OR_TKT_LOST, tk) || is_reason(OR_STEPDOWN, tk)) &&
+ check_attr_prereq(tk, GRANT_AUTO)) {
+ tk_log_info("attribute prerequisite not met, "
+ "not starting elections");
+ return 0;
+ }
+
/* elections were already started, but not yet finished/timed out */
if (is_time_set(&tk->election_end) && !is_past(&tk->election_end))
return 1;
@@ -747,6 +756,9 @@
} else {
tk_log_debug("starting elections");
}
+ tk_log_debug("elections caused by %s %s",
+ state_to_string(reason),
+ reason == OR_AGAIN ? state_to_string(tk->election_reason) : "" );
}
/* §5.2 */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth/src/ticket.c new/booth/src/ticket.c
--- old/booth/src/ticket.c 2015-11-06 09:20:07.000000000 +0100
+++ new/booth/src/ticket.c 2015-11-23 19:13:11.000000000 +0100
@@ -230,6 +230,41 @@
return rv;
}
+#define attr_found(geo_ap, ap) \
+ ((geo_ap) && !strcmp((geo_ap)->val, (ap)->attr_val))
+
+int check_attr_prereq(struct ticket_config *tk, grant_type_e grant_type)
+{
+ GList *el;
+ struct attr_prereq *ap;
+ struct geo_attr *geo_ap;
+
+ for (el = g_list_first(tk->attr_prereqs); el; el = g_list_next(el))
+ {
+ ap = (struct attr_prereq *)el->data;
+ if (ap->grant_type != grant_type)
+ continue;
+ geo_ap = (struct geo_attr *)g_hash_table_lookup(tk->attr, ap->attr_name);
+ switch(ap->op) {
+ case ATTR_OP_EQ:
+ if (!attr_found(geo_ap, ap))
+ goto fail;
+ break;
+ case ATTR_OP_NE:
+ if (attr_found(geo_ap, ap))
+ goto fail;
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+
+fail:
+ tk_log_warn("'%s' attr-prereq failed", ap->attr_name);
+ return 1;
+}
+
/* do we need to run the external program?
* or we already done that and waiting for the outcome
* or program exited and we can collect the status
@@ -278,6 +313,9 @@
{
int rv;
+ if (reason == OR_ADMIN && check_attr_prereq(tk, GRANT_MANUAL))
+ return RLT_ATTR_PREREQ;
+
switch(do_ext_prog(tk, 0)) {
case 0:
/* everything fine */
@@ -887,10 +925,13 @@
static void ticket_lost(struct ticket_config *tk)
{
+ int reason = OR_TKT_LOST;
+
if (tk->leader != local) {
tk_log_warn("lost at %s", site_string(tk->leader));
} else {
tk_log_warn("lost majority (revoking locally)");
+ reason = tk->election_reason ? tk->election_reason : OR_REACQUIRE;
}
tk->lost_leader = tk->leader;
@@ -899,7 +940,7 @@
set_state(tk, ST_FOLLOWER);
if (local->type == SITE) {
ticket_write(tk);
- schedule_election(tk, OR_TKT_LOST);
+ schedule_election(tk, reason);
}
}
@@ -1180,8 +1221,7 @@
case ST_FOLLOWER:
/* If there is (or should be) some owner, check on it later on.
* If no one is interested - don't care. */
- if (is_owned(tk) &&
- (local->type == SITE)) {
+ if (is_owned(tk)) {
interval_add(&tk->term_expires, tk->acquire_after, &tv);
ticket_next_cron_at(tk, &tv);
}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth/src/ticket.h new/booth/src/ticket.h
--- old/booth/src/ticket.h 2015-11-06 09:20:07.000000000 +0100
+++ new/booth/src/ticket.h 2015-11-23 19:13:11.000000000 +0100
@@ -106,6 +106,8 @@
void add_random_delay(struct ticket_config *tk);
void schedule_election(struct ticket_config *tk, cmd_reason_t reason);
+int check_attr_prereq(struct ticket_config *tk, grant_type_e grant_type);
+
static inline void ticket_next_cron_at(struct ticket_config *tk, timetype *when)
{
copy_time(when, &tk->next_cron);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth/src/transport.c new/booth/src/transport.c
--- old/booth/src/transport.c 2015-11-06 09:20:07.000000000 +0100
+++ new/booth/src/transport.c 2015-11-23 19:13:11.000000000 +0100
@@ -236,10 +236,15 @@
}
/* First try with exact addresses, then optionally with subnet matching. */
- if (ifa->ifa_prefixlen > address_bits_matched)
+ if (ifa->ifa_prefixlen > address_bits_matched) {
find_address(ipaddr,
ifa->ifa_family, ifa->ifa_prefixlen,
fuzzy_allowed, &me, &address_bits_matched);
+ if (me) {
+ log_debug("found myself at %s (%d bits matched)",
+ site_string(me), address_bits_matched);
+ }
+ }
}
h = NLMSG_NEXT(h, status);
}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth/test/live_test.sh new/booth/test/live_test.sh
--- old/booth/test/live_test.sh 2015-11-06 09:20:07.000000000 +0100
+++ new/booth/test/live_test.sh 2015-11-23 19:13:11.000000000 +0100
@@ -370,7 +370,26 @@
/^ticket.*'$tkt'/ {n=1}
' $cnf
}
+get_attr() {
+ awk '
+n && /^[[:space:]]*attr-prereq = auto .* eq / {print $4,$6; exit}
+n && (/^$/ || /^ticket.*/) {exit}
+/^ticket.*'$tkt'/ {n=1}
+' $cnf
+}
+set_site_attr() {
+ local site
+ site=`get_site $1`
+ set -- `get_attr`
+ geostore set -s $site $1 $2
+}
+del_site_attr() {
+ local site
+ site=`get_site $1`
+ set -- `get_attr`
+ geostore delete -s $site $1
+}
break_external_prog() {
run_site $1 crm configure "location $PREFNAME `get_rsc` rule -inf: defined \#uname"
}
@@ -507,7 +526,7 @@
# list on all of them (at least one should have booth
# running)
ticket_line=`forall_sites booth list | grep $tkt | sort -u | head -1`
- grantee=`echo "$ticket_line" | sed 's/.*leader: //;s/,.*//'`
+ grantee=`echo "$ticket_line" | sed 's/.*leader: //;s/,.*//;s/NONE/none/'`
echo $grantee
[ "$grantee" = "none" ] && return
! echo "$ticket_line" | grep -q "$tkt.*pending"
@@ -948,6 +967,8 @@
# ticket failover
setup_failover() {
grant_ticket 1
+ [ -n "`get_attr`" ] && set_site_attr 2
+ return 0
}
test_failover() {
stop_site_clean `get_site 1` || return 1
@@ -969,6 +990,8 @@
# split brain (leader alone)
setup_split_leader() {
grant_ticket_cib 1
+ [ -n "`get_attr`" ] && set_site_attr 2
+ return 0
}
test_split_leader() {
run_site 1 $iprules stop $port >/dev/null
@@ -1029,6 +1052,7 @@
# external test prog failed
setup_external_prog_failed() {
grant_ticket 1 || return 1
+ [ -n "`get_attr`" ] && set_site_attr 2
break_external_prog 1
show_pref 1 || return 1
}
@@ -1047,6 +1071,57 @@
[ -n "`get_rsc`" ]
}
+## TEST: attr_prereq_ok ##
+
+# failover with attribute prerequisite
+setup_attr_prereq_ok() {
+ grant_ticket 1 || return 1
+ set_site_attr 2
+ stop_site_clean `get_site 1`
+ booth_status `get_site 1` && return 1
+ return 0
+}
+test_attr_prereq_ok() {
+ wait_exp
+ wait_timeout
+}
+check_attr_prereq_ok() {
+ check_consistency `get_site 2`
+}
+recover_attr_prereq_ok() {
+ start_site `get_site 1`
+ del_site_attr 2
+}
+applicable_attr_prereq_ok() {
+ [ -n "`get_attr`" ]
+}
+
+## TEST: attr_prereq_fail ##
+
+# failover with failed attribute prerequisite
+setup_attr_prereq_fail() {
+ grant_ticket 1 || return 1
+ del_site_attr 2 >/dev/null 2>&1
+ stop_site_clean `get_site 1`
+ booth_status `get_site 1` && return 1
+ return 0
+}
+test_attr_prereq_fail() {
+ wait_exp
+ wait_exp
+ wait_exp
+}
+check_attr_prereq_fail() {
+ check_consistency &&
+ booth_where_granted | grep -qwi none
+}
+recover_attr_prereq_fail() {
+ start_site `get_site 1`
+}
+applicable_attr_prereq_fail() {
+ [ -n "`get_attr`" ]
+}
+
#
# environment modifications
#
@@ -1161,7 +1236,7 @@
simultaneous_start_even slow_start_granted
restart_granted reload_granted restart_granted_nocib restart_notgranted
failover split_leader split_follower split_edge
-external_prog_failed"}
+external_prog_failed attr_prereq_ok attr_prereq_fail"}
for t in $TESTS; do
runtest $t