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 - dmuhameda...@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 - dmuhameda...@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 = <grant_type> <name> <op> <value>
+
+       <grant_type>: "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 <stdio.h>
 #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 <stdlib.h>
 #include <errno.h>
 #include <inttypes.h>
+#include <unistd.h>
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#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


Reply via email to