[Cluster-devel] cluster/rgmanager/src/daemons Makefile test.c ...
lhh at sourceware.org
lhh at sourceware.org
Tue Feb 20 19:56:19 UTC 2007
CVSROOT: /cvs/cluster
Module name: cluster
Changes by: lhh at sourceware.org 2007-02-20 19:56:18
Modified files:
rgmanager/src/daemons: Makefile test.c
Added files:
rgmanager/src/daemons: depends.c dtest.c
Log message:
Initial checkin of simple dependency engine
Patches:
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/rgmanager/src/daemons/depends.c.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/rgmanager/src/daemons/dtest.c.diff?cvsroot=cluster&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/rgmanager/src/daemons/Makefile.diff?cvsroot=cluster&r1=1.15&r2=1.16
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/rgmanager/src/daemons/test.c.diff?cvsroot=cluster&r1=1.6&r2=1.7
/cvs/cluster/cluster/rgmanager/src/daemons/depends.c,v --> standard output
revision 1.1
--- cluster/rgmanager/src/daemons/depends.c
+++ - 2007-02-20 19:56:19.181520000 +0000
@@ -0,0 +1,2542 @@
+
+/**
+ Copyright Red Hat, Inc. 2002-2003, 2006
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
+ MA 02139, USA.
+*/
+/** @file
+ * CCS dependency parsing, based on failover domain parsing
+ * Transition generation based on simple brute-force / best-first
+ * tree search.
+ *
+ * Allows specification of two types of rules concerning service states:
+ *
+ * - requirement: A -> B means B must be running either for A to start or
+ * at all times (if B stops, A will be stopped)
+ * - colocation: A -> B means that A must be run on the same node as B
+ * (note that colocation doesn't mean that B must be running;
+ * to do both, you must enable a requirement).
+ * A X> B means that A must be run on a different node from B.
+ */
+#include <string.h>
+#include <list.h>
+#include <clulog.h>
+#include <resgroup.h>
+#include <reslist.h>
+#include <ccs.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <members.h>
+#include <reslist.h>
+#include <depends.h>
+#include <ctype.h>
+
+struct _try {
+ dep_op_t *ops;
+ int score;
+ int depth;
+};
+
+void deconstruct_dep(dep_t *dep);
+int dep_graph_validate(dep_t **deps);
+fod_t *fod_find_domain(fod_t **domains, char *name);
+
+//#define DEBUG
+
+#ifdef DEBUG
+#define ENTER() clulog(LOG_DEBUG, "ENTER: %s\n", __FUNCTION__)
+#define RETURN(val) {\
+ clulog(LOG_DEBUG, "RETURN: %s line=%d value=%d\n", __FUNCTION__, \
+ __LINE__, (val));\
+ return(val);\
+}
+#else
+#define ENTER()
+#define RETURN(val) return(val)
+#endif
+
+#ifdef NO_CCS
+#define ccs_get(fd, query, ret) conf_get(query, ret)
+#endif
+
+/*
+ <dependencies>
+ <dependency name="service:bar">
+ <target name="service:foo" colocate="always|never|unspec"
+ require="start|always|unspec"/>
+ </dependency>
+ </dependencies>
+ */
+
+static dep_node_t *
+get_dep_node(int ccsfd, char *base, int idx, dep_t *dep, int *_done)
+{
+ dep_node_t *dn;
+ char xpath[256];
+ char *ret;
+ int c;
+
+ *_done = 0;
+ snprintf(xpath, sizeof(xpath), "%s/target[%d]/@name",
+ base, idx);
+ if (ccs_get(ccsfd, xpath, &ret) != 0) {
+ *_done = 1;
+ return NULL;
+ }
+
+ list_for(&dep->d_nodes, dn, c) {
+ if (strcasecmp(ret, dn->dn_name))
+ continue;
+
+ printf("#XX: Target %s defined multiple times in "
+ "dependency block %s\n", ret, dep->d_name);
+ free(ret);
+ return NULL;
+ }
+
+ dn = malloc(sizeof(*dn));
+ if (!dn)
+ return NULL;
+ memset(dn, 0, sizeof(*dn));
+
+ /* Already malloc'd; simply store */
+ dn->dn_name = ret;
+ dn->dn_ptr = NULL;
+ dn->dn_req = DEP_REQ_UNSPEC;
+ dn->dn_colo = DEP_COLO_UNSPEC;
+
+ snprintf(xpath, sizeof(xpath), "%s/target[%d]/@require",
+ base, idx);
+ if (ccs_get(ccsfd, xpath, &ret) == 0 && ret) {
+
+ if (!strcasecmp(ret, "start")) {
+ dn->dn_req = DEP_REQ_START;
+ } else if (!strcasecmp(ret, "always")) {
+ dn->dn_req = DEP_REQ_ALWAYS;
+ } else if (!strcasecmp(ret, "unspec")) {
+ dn->dn_req = DEP_REQ_UNSPEC;
+ } else {
+ dn->dn_req = atoi(ret);
+ }
+
+ free(ret);
+ }
+
+ snprintf(xpath, sizeof(xpath), "%s/target[%d]/@colocate",
+ base, idx);
+ if (ccs_get(ccsfd, xpath, &ret) == 0 && ret) {
+
+ if (!strcasecmp(ret, "never")) {
+ dn->dn_colo = DEP_COLO_NEVER;
+ } else if (!strcasecmp(ret, "always")) {
+ dn->dn_colo = DEP_COLO_ALWAYS;
+ } else if (!strcasecmp(ret, "unspec")) {
+ dn->dn_colo = DEP_COLO_UNSPEC;
+ } else {
+ dn->dn_colo = atoi(ret);
+ }
+
+ free(ret);
+ }
+
+ if (dn->dn_req == DEP_REQ_UNSPEC &&
+ dn->dn_colo == DEP_COLO_UNSPEC) {
+ printf("Dropping dependency target %s: no rule in use\n",
+ dn->dn_name);
+ free(dn->dn_name);
+ free(dn);
+ return NULL;
+ }
+
+ return dn;
+}
+
+
+static dep_t *
+get_dep(int ccsfd, char *base, int idx, dep_t **deps, int *_done)
+{
+ dep_t *dep;
+ dep_node_t *dn;
+ char xpath[256];
+ char *ret;
+ int x = 1, c;
+ int done;
+
+ *_done = 0;
+ snprintf(xpath, sizeof(xpath), "%s/dependency[%d]/@name",
+ base, idx);
+ if (ccs_get(ccsfd, xpath, &ret) != 0) {
+ *_done = 1;
+ return NULL;
+ }
+
+ list_for(deps, dep, c) {
+ if (strcasecmp(dep->d_name, ret))
+ continue;
+
+ printf("#XX: Dependency block %s defined multiple "
+ "times\n", ret);
+ free(ret);
+ return NULL;
+ }
+
+ dep = malloc(sizeof(*dep));
+ if (!dep)
+ return NULL;
+ memset(dep, 0, sizeof(*dep));
+ dep->d_name = ret;
+ dep->d_nodes = NULL;
+ dep->d_flags = 0;
+ dep->d_hits = 0;
+ dep->d_deps = 0;
+
+ snprintf(xpath, sizeof(xpath), "%s/dependency[%d]",
+ base, idx);
+
+ do {
+ dn = get_dep_node(ccsfd, xpath, x++, dep, &done);
+ if (dn) {
+ dep->d_deps++;
+ list_insert(&dep->d_nodes, dn);
+ }
+ } while (!done);
+
+ if (!dep->d_nodes) {
+ printf("Dropping dependency block %s: No targets\n",
+ dep->d_name);
+ free(dep->d_name);
+ free(dep);
+ return NULL;
+ }
+
+ return dep;
+}
+
+
+/**
+ * Constructs links in the dependency tree
+ */
+static void
+dep_construct_tree(dep_t **deps)
+{
+ dep_t *curr, *curr2, *dep;
+ dep_node_t *dn;
+ int done, found, a, b, c;
+
+ do {
+ done = 1;
+ list_for(deps, curr, a) {
+ list_for(&curr->d_nodes, dn, b) {
+ found = 0;
+ list_for(deps, curr2, c) {
+ if (!strcasecmp(curr2->d_name,
+ dn->dn_name)) {
+ if (!dn->dn_ptr) {
+ dn->dn_ptr = curr2;
+ dn->dn_ptr->d_hits++;
+ }
+ found = 1;
+ }
+ }
+
+ if (!found) {
+ done=0;
+
+ dep = malloc(sizeof(*dep));
+ if (!dep)
+ return;
+ memset(dep, 0, sizeof(*dep));
+ dep->d_name = strdup(dn->dn_name);
+ if (!dep->d_name)
+ return;
+ dep->d_nodes = NULL;
+ dep->d_flags = DEP_FLAG_IMPLIED;
+ dep->d_hits = 1;
+ dep->d_deps = 0;
+ dn->dn_ptr = dep;
+
+ list_insert(deps, dep);
+
+ break;
+ }
+ }
+
+ if (!done)
+ break;
+ }
+ } while (!done);
+}
+
+
+/**
+ * similar API to failover domain
+ */
+int
+construct_depends(int ccsfd, dep_t **deps)
+{
+ char xpath[256];
+ int x = 1, done, c;
+ dep_t *dep, *curr;
+
+ snprintf(xpath, sizeof(xpath),
+ RESOURCE_TREE_ROOT "/dependencies");
+
+ do {
+ dep = get_dep(ccsfd, xpath, x++, deps, &done);
+ if (dep) {
+ list_insert(deps, dep);
+ }
+ } while (!done);
+
+ /* Insert terminal dependency nodes & construct tree */
+ /* XXX optimize */
+ dep_construct_tree(deps);
+
+ dep_graph_validate(deps);
+
+ do {
+ done = 1;
+ list_for(deps, curr, c) {
+ if (curr->d_flags &
+ (DEP_FLAG_CYCLIC | DEP_FLAG_IMPOSSIBLE)) {
+ printf("Removing dependency block %s: "
+ "Invalid\n", curr->d_name);
+ done = 0;
+
+ list_remove(deps, curr);
+ deconstruct_dep(curr);
+ break;
+ }
+ }
+ } while (!done);
+
+ return 0;
+}
+
+
+void
+deconstruct_dep(dep_t *dep)
+{
+ dep_node_t *node;
+
+ while ((node = dep->d_nodes)) {
+ list_remove(&dep->d_nodes, node);
+ if (node->dn_name)
+ free(node->dn_name);
+ free(node);
+ }
+
+ if (dep->d_name)
+ free(dep->d_name);
+ free(dep);
+}
+
+
+void
+deconstruct_depends(dep_t **deps)
+{
+ dep_t *dep = NULL;
+
+ while ((dep = *deps)) {
+ list_remove(deps, dep);
+ deconstruct_dep(dep);
+ }
+}
+
+
+static inline dep_rs_t *
+_find_state(char *name, dep_rs_t *sl, int slen)
+{
+ int x;
+
+ for (x = 0; x < slen; x++)
+ if (!strcasecmp(sl[x].rs_status.rs_name, name))
+ return &sl[x];
+
+ return NULL;
+}
+
+
+static int
+rs_running(char *name, dep_rs_t *sl, int slen)
+{
+ int x;
+ dep_rs_t *rs = NULL;
+
+ if (name) {
+ rs = _find_state(name, sl, slen);
+ } else if (slen == 1) {
+ rs = sl;
+ }
+
+ if (rs) {
+ if (rs->rs_flags & RS_BROKEN)
+ return -1;
+ return (rs->rs_status.rs_state == RG_STATE_STARTING ||
+ rs->rs_status.rs_state == RG_STATE_STARTED ||
+ rs->rs_status.rs_state == RG_STATE_STOPPING );
+ }
+
+ return 0;
+}
+
+
+int
+dep_tree_dup(dep_t **dest, dep_t **src)
+{
+ dep_t *dep, *dep_cpy;
+ dep_node_t *dn, *dn_cpy;
+ int a, b;
+
+ list_for(src, dep, a) {
+ while ((dep_cpy = malloc(sizeof(dep_t))) == NULL)
+ usleep(10000);
+ memset(dep_cpy, 0, sizeof(dep_t));
+ while ((dep_cpy->d_name = strdup(dep->d_name)) == NULL)
+ usleep(10000);
+
+ dep_cpy->d_flags &= ~(DEP_FLAG_ALWAYS|DEP_FLAG_NEVER);
+
+ list_for(&dep->d_nodes, dn, b) {
+ while ((dn_cpy = malloc(sizeof(dep_node_t))) == NULL)
+ usleep(10000);
+ memset(dn_cpy, 0, sizeof(dep_node_t));
+ while ((dn_cpy->dn_name = strdup(dn->dn_name)) == NULL)
+ usleep(10000);
+ dn_cpy->dn_colo = dn->dn_colo;
+ dn_cpy->dn_req = dn->dn_req;
+
+ /* Flags are all errors right now... */
+ /* dn_cpy->dn_flags = dn->dn_flags; */
+ list_insert(&dep_cpy->d_nodes, dn_cpy);
+ }
+
+ list_insert(dest, dep_cpy);
+ }
+
+ /* Construct internal tree */
+ dep_construct_tree(dest);
+
+ return 0;
+}
+
+
+static void
+dep_tree_print(FILE *fp, dep_t *start, dep_t *dep, int level)
+{
+ dep_node_t *dn = NULL;
+ int x, c;
+
+ if (!dep)
+ return;
+
+ if ((dep == start) && level) {
+ fprintf(fp, "[cycles to %s]\n", start->d_name);
+ return;
+ }
+
+ fprintf(fp, "%s\n",dep->d_name);
+
+ if (dep->d_nodes) {
+ list_for(&dep->d_nodes, dn, c) {
+ for (x = 0; x < level; x++)
+ fprintf(fp, " ");
+ fprintf(fp, "|\n");
+ for (x = 0; x < level; x++)
+ fprintf(fp, " ");
+ fprintf(fp, "+-");
+ switch(dn->dn_colo) {
+ case DEP_COLO_UNSPEC:
+ fprintf(fp, "--");
+ break;
+ case DEP_COLO_ALWAYS:
+ fprintf(fp, "Ca");
+ break;
+ case DEP_COLO_NEVER:
+ fprintf(fp, "Cn");
+ break;
+ }
+ switch(dn->dn_req) {
+ case DEP_REQ_UNSPEC:
+ fprintf(fp, "--");
+ break;
+ case DEP_REQ_START:
+ fprintf(fp, "Rs");
+ break;
+ case DEP_REQ_ALWAYS:
+ fprintf(fp, "Ra");
+ break;
+ }
+ fprintf(fp, "-> ");
+
+ if (dn->dn_traversed) {
+ fprintf(fp, "[cycles to %s]\n",
+ dn->dn_ptr->d_name);
+ } else {
+ dn->dn_traversed = 1;
+ dep_tree_print(fp, start, dn->dn_ptr, level+1);
+ dn->dn_traversed = 0;
+ }
+
+ }
+ }
+}
+
+
+static void
+_dot_clean_string(char *str)
+{
+ int x;
+
+ /*
+ if (strncmp(str, "service:", 8) == 0)
+ memmove(str, str+8, strlen(str)-8+1);
+ */
+
+ if (!isalpha(str[0]))
+ str[0] = '_';
+
+ for (x = 0; x < strlen(str); x++) {
+ if (isalnum(str[x]))
+ continue;
+ if (str[x] == '_' || str[x] == '-')
+ continue;
+ str[x] = '_';
+ }
+}
+
+
+void
+print_dep_tree_dot(FILE *fp, dep_t *start, dep_t *dep, int level)
+{
+ dep_node_t *dn = NULL;
+ char src[64], dest[64];
+ int c;
+
+ if (!dep)
+ return;
+
+ if ((dep == start) && level)
+ return;
+
+ if (dep->d_nodes) {
+ list_for(&dep->d_nodes, dn, c) {
+ if (dn->dn_traversed)
+ continue;
+
+ strncpy(src, dep->d_name, sizeof(src));
+ strncpy(dest, dn->dn_name, sizeof(dest));
+ _dot_clean_string(src);
+ _dot_clean_string(dest);
+
+ dn->dn_traversed = 1;
+ switch(dn->dn_colo) {
+ case DEP_COLO_UNSPEC:
+ break;
+ case DEP_COLO_ALWAYS:
+ fprintf(fp, "\tedge [color=black, "
+ "arrowhead=diamond, label=\"Ca\"");
+ if (dn->dn_flags & DN_BROKEN_COLO)
+ fprintf(fp, ", style=dashed");
+ else
+ fprintf(fp, ", style=solid");
+ fprintf(fp, "];\n");
+ fprintf(fp, "\t%s -> %s;\n", src, dest);
+ break;
+ case DEP_COLO_NEVER:
+ fprintf(fp, "\tedge [color=red, "
+ "arrowhead=diamond, label=\"Cn\"");
+ if (dn->dn_flags & DN_BROKEN_NONCOLO)
+ fprintf(fp, ", style=dashed");
+ else
+ fprintf(fp, ", style=solid");
+ fprintf(fp, "];\n");
+ fprintf(fp, "\t%s -> %s;\n", src, dest);
+ break;
+ }
+ switch(dn->dn_req) {
+ case DEP_REQ_UNSPEC:
+ break;
+ case DEP_REQ_START:
+ fprintf(fp, "\tedge [color=green, "
+ "arrowhead=vee, label=\"Rs\"");
+ if (dn->dn_flags & DN_BROKEN_REQ)
+ fprintf(fp, ", style=dashed");
+ else
+ fprintf(fp, ", style=solid");
+ fprintf(fp, "];\n");
+ fprintf(fp, "\t%s -> %s;\n", src, dest);
+ break;
+ case DEP_REQ_ALWAYS:
+ fprintf(fp, "\tedge [color=black, "
+ "arrowhead=vee, label=\"Ra\"");
+ if (dn->dn_flags & DN_BROKEN_REQ)
+ fprintf(fp, ", style=dashed");
+ else
+ fprintf(fp, ", style=solid");
+ fprintf(fp, "];\n");
+ fprintf(fp, "\t%s -> %s;\n", src, dest);
+ break;
+ }
+
+ print_dep_tree_dot(fp, start, dn->dn_ptr, level+1);
+
+ }
+ }
+}
+
+
+/**
+ * Check for cyclic 'require' dependency.
+ */
+static int
+find_cyclic_req(dep_t *dep, dep_t *what)
+{
+ dep_node_t *dn;
+ int ret, c;
+
+ if (dep == what)
+ return 2;
+
+ if (!dep->d_nodes)
+ return 0;
+
+ /* initialize */
+ if (what == NULL)
+ what = dep;
+
+ list_for(&dep->d_nodes, dn, c) {
+
+ if (!dn->dn_traversed &&
+ dn->dn_req != DEP_REQ_UNSPEC) {
+ dn->dn_traversed = 1;
+ ret = find_cyclic_req(dn->dn_ptr, what);
+ dn->dn_traversed = 0;
+
+ if (ret == 2) {
+ printf("Error: Cyclic Requirement: \n");
+ printf(" %s ... -> %s -> %s\n",
+ what->d_name,
+ dep->d_name,
+ dn->dn_ptr->d_name);
+ }
+ if (ret)
+ return 1;
+ } else if (dn->dn_traversed)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * Tag all nodes which have colocation requirements
+ */
+static int
+tag_colo(dep_t *dep, dep_t *what, dep_colo_t colo)
+{
+ dep_node_t *dn;
+ int ret = 0, c;
+
+ /* How did we get here? */
+ switch(colo) {
+ case DEP_COLO_ALWAYS:
+ if (dep->d_flags & DEP_FLAG_NEVER)
+ ret = 1;
+ dep->d_flags |= DEP_FLAG_ALWAYS;
+ break;
+ case DEP_COLO_NEVER:
+ if (dep->d_flags & DEP_FLAG_ALWAYS)
+ ret = 1;
+ dep->d_flags |= DEP_FLAG_NEVER;
+ break;
+ default:
+ break;
+ }
+
+ if (dep == what)
+ goto out;
+
+ if (!dep->d_nodes)
+ goto out;
+
+ /* Start if this is the first call*/
+ if (what == NULL)
+ what = dep;
+
+ list_for(&dep->d_nodes, dn, c) {
+
+ if (!dn->dn_traversed) {
+ dn->dn_traversed = 1;
+ ret += tag_colo(dn->dn_ptr, what, dn->dn_colo);
+ dn->dn_traversed = 0;
+ }
+
+ }
+
+out:
+ if (ret)
+ dep->d_flags |= DEP_FLAG_IMPOSSIBLE;
+ return ret;
+}
+
+
+int
+dep_print_dep_errors(dep_t **deps)
+{
+ dep_t *dep;
+ dep_node_t *dn;
+ int a, b;
+
+ list_for(deps, dep, a) {
+ list_for(&dep->d_nodes, dn, b) {
+ if (dn->dn_flags & DN_BROKEN_COLO) {
+ printf("Resource %s must colocate with "
+ "resource %s\n", dep->d_name,
+ dn->dn_name);
+ }
+ if (dn->dn_flags & DN_BROKEN_NONCOLO) {
+ printf("Resource %s must never colocate with "
+ "resource %s\n", dep->d_name,
+ dn->dn_name);
+ }
+ if (dn->dn_flags & DN_BROKEN_REQ) {
+ printf("Resource %s depends on "
+ "resource %s\n", dep->d_name,
+ dn->dn_name);
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+int
+dep_print_state_errors(dep_rs_t *rs, int slen)
+{
+ int x;
+
+ for (x = 0; x < slen; x++) {
+ if (rs[x].rs_flags & RS_ILLEGAL_NODE)
+ printf("Resource %s on illegal node %d\n",
+ rs[x].rs_status.rs_name,
+ rs[x].rs_status.rs_owner);
+ if (rs[x].rs_flags & RS_DEAD_NODE)
+ printf("Resource %s on dead/offline node %d\n",
+ rs[x].rs_status.rs_name,
+ rs[x].rs_status.rs_owner);
+ }
+ return 0;
+}
+
+
+/**
+ * clear our loop detection
+ */
+int
+dep_clear_dep_errors(dep_t **deps)
+{
+ dep_t *dep;
+ dep_node_t *dn;
+ int a, b;
+
+ list_for(deps, dep, a) {
+ dep->d_flags &= ~(DEP_FLAG_ALWAYS | DEP_FLAG_NEVER);
+
+ list_for(&dep->d_nodes, dn, b) {
+ dn->dn_traversed = 0;
+ dn->dn_flags = 0;
+ }
+ }
+
+ return 0;
+}
+
+
+int
+dep_clear_traversed(dep_t **deps)
+{
+ dep_t *dep;
+ dep_node_t *dn;
+ int a, b;
+
+ list_for(deps, dep, a) {
+ list_for(&dep->d_nodes, dn, b) {
+ dn->dn_traversed = 0;
+ }
+ }
+
+ return 0;
+}
+
+
+int
+dep_clear_state_errors(dep_rs_t *rs, int slen)
+{
+ int x;
+ for (x = 0; x < slen; x++)
+ rs[x].rs_flags &= ~(RS_ILLEGAL_NODE|RS_DEAD_NODE);
+ return 0;
+}
+
+
+void
+dep_reset(dep_t **deps, dep_rs_t *rs, int slen)
+{
+ dep_clear_dep_errors(deps);
+ dep_clear_state_errors(rs, slen);
+}
+
+
+void
+dep_print_errors(dep_t **deps, dep_rs_t *rs, int slen)
+{
+ dep_print_dep_errors(deps);
+ dep_print_state_errors(rs, slen);
+}
+
+
+int
+dep_graph_validate(dep_t **deps)
+{
+ dep_t *dep;
+ dep_flag_t f;
+ int a;
+
+ list_for(deps, dep, a) {
+
+ if (find_cyclic_req(dep, NULL)) {
+ printf("Cyclic requirement dependency in block %s\n",
+ dep->d_name);
+ dep->d_flags |= DEP_FLAG_CYCLIC;
+ }
+
+ tag_colo(dep, NULL, 0);
+ }
+
+ f = (DEP_FLAG_ALWAYS | DEP_FLAG_NEVER);
+ list_for(deps, dep, a) {
+ if (((dep->d_flags & f) == f) ||
+ (dep->d_flags & DEP_FLAG_IMPOSSIBLE)) {
+ printf("Graph %s: colocation conflict\n", dep->d_name);
+ dep->d_flags |= DEP_FLAG_IMPOSSIBLE;
+ }
+ }
+
+ return 0;
+}
+
+
+void
+print_depends(FILE *fp, dep_t **deps)
+{
+ dep_t *dep;
+ int a;
+
+ list_for(deps, dep, a) {
+ if (dep->d_nodes && !(dep->d_flags & DEP_FLAG_IMPLIED) &&
+ dep->d_hits == 0) {
+ dep_tree_print(fp, dep, dep, 0);
+ fprintf(fp,"\n");
+ }
+ }
+}
+
+
+static void
+_print_depends_dot(FILE *fp, dep_t **deps)
+{
+ dep_t *dep;
+ int a;
+
+ list_for(deps, dep, a) {
+ print_dep_tree_dot(fp, dep, dep, 0);
+ }
+
+ dep_clear_traversed(deps);
+}
+
+
+void
+print_depends_dot(FILE *fp, dep_t **deps)
+{
+ fprintf(fp, "digraph G {\n");
+ _print_depends_dot(fp, deps);
+ fprintf(fp, "}\n");
+}
+
+
+/**
+ * check to ensure a resource is on an allowed node.
+ * Note - makes no assumption about the actual resource state; it could be
+ * in the stopped state.
+ */
+int
+dep_check_res_loc(dep_rs_t *state)
+{
+ int x;
+
+ if (!rs_running(NULL, state, 1)) {
+ /* Not running = location is perfectly fine ... sort of */
+ return 0;
+ }
+
+ if (!state->rs_allowed || state->rs_allowed_len <= 0)
+ return 0;
+
+ for (x = 0; x < state->rs_allowed_len; x++) {
+ if (state->rs_status.rs_owner == state->rs_allowed[x])
+ return 0;
+ }
+
+ /* Didn't match a legal node -> fail */
+ state->rs_flags |= RS_ILLEGAL_NODE;
+ return 1;
+}
+
+
+static dep_t *
+find_dep(dep_t **deps, char *name)
+{
+ dep_t *dep = NULL;
+ int a;
+
+ if (!deps)
+ return NULL;
+
+ list_for(deps, dep, a) {
+ if (!strcasecmp(dep->d_name, name))
+ return dep;
+ }
+
+ return NULL;
+}
+
+
+/**
+ * traverses the requirements tree looking for 'name'
+ * returns 1 if found, 0 if not
+ */
+static int
+_tree_walk_req(dep_t *dep, dep_rs_t *state, dep_rs_t *sl, int slen)
+{
+ dep_node_t *dn;
+ int errors = 0, ret, a;
+
+ if (!dep || !dep->d_nodes)
+ return 0;
+
+ list_for(&dep->d_nodes, dn, a) {
+
+ /* If we have a specified requirement... */
+ ret = 0;
+ if (!dn->dn_traversed &&
+ dn->dn_req != DEP_REQ_UNSPEC) {
+
+ /* see if our child is running. If not, we're done */
+ ret = rs_running(dn->dn_name, sl, slen);
+
+ /* XXX check for req-start vs. req-always */
+ if (ret <= 0) {
+ dn->dn_flags |= DN_BROKEN_REQ;
+ state->rs_flags |= RS_BROKEN;
+ ret = 1;
+ } else {
+ dn->dn_traversed = 1;
+ ret = _tree_walk_req(dn->dn_ptr,
+ _find_state(dn->dn_name, sl, slen), sl, slen);
+ dn->dn_traversed = 0;
+ }
+
+ if (ret) {
+ errors += ret;
+ dn->dn_flags |= DN_BROKEN_REQ;
+ state->rs_flags |= RS_BROKEN;
+ }
+ }
+ }
+
+ return errors;
+}
+
+/*
+ * Returns nonzero if something requires this resource, 0 if not
+ */
+int
+dep_check_requires(dep_t **deps, dep_rs_t *state, dep_rs_t *states, int slen)
+{
+ dep_t *dep;
+ dep_node_t *dn;
+ int errors = 0, x, a;
+
+ /* Check to see if anything depends on this (not-running) resource */
+ list_for(deps, dep, a) {
+ errors += _tree_walk_req(dep, NULL, /*state->rs_status.rs_name,*/
+ states, slen);
+ }
+
+ /* Flag requirements on any broken-dep resources as broken, too... */
+ dep_clear_traversed(deps);
+
+ return errors;
+}
+
+
+/**
+ * Check to see if this resource is causing a conflict. It can cause a
+ * conflict if it is stopped but other resources depend on it.
+ * If it is running, it can not cause a requirement conflict, but it might
+ * cause a colocation conflict.
+ */
+static int
+_dep_check_requires(dep_t **deps, dep_rs_t *state, dep_rs_t *states, int slen)
+{
+ /* if not running, it has no dependencies */
+ if (!rs_running(NULL, state, 1))
+ return 0;
+
+ return _tree_walk_req(find_dep(deps, state->rs_status.rs_name),
+ state, states, slen);
+
+ //return dep_check_requires(deps, state, states, slen);
+}
+
+/**
+ * Checks the tree to see if there's a colocation requiremet on the given
+ * resource. Returns 1 if found, 0 if not.
+ * returns 1 if found, 0 if not We don't need to recurse on this one.
+ */
+static int
+_tree_walk_colo(dep_t *dep, dep_t *start, char *name, dep_rs_t *sl, int slen)
+{
+ dep_node_t *dn;
+ dep_rs_t *state;
+ int errors = 0, a;
+
+ if (dep == start)
+ return 0;
+
+ if (!dep->d_nodes)
+ return 0;
+
+ /* Start if this is the first call*/
+ if (start == NULL)
+ start = dep;
+
+ state = _find_state(dep->d_name, sl, slen);
+ if (!state)
+ return 0;
+
+ list_for(&dep->d_nodes, dn, a) {
+
+ /* If we have a specified requirement... */
+ if (!dn->dn_traversed &&
+ dn->dn_colo == DEP_COLO_ALWAYS) {
+
+ if (!strcasecmp(dn->dn_name, name) &&
+ !(dn->dn_flags & DN_BROKEN_COLO)) {
+ /* Broken colocation req */
+ state->rs_flags |= RS_BROKEN;
+ dn->dn_flags |= DN_BROKEN_COLO;
+ ++errors;
+ }
+ dn->dn_traversed = 1;
+ errors += _tree_walk_colo(dn->dn_ptr, start, name,
+ sl, slen);
+ dn->dn_traversed = 0;
+ }
+ }
+
+ return errors;
+}
+
+
+/**
+ * Checks the immediate children of the given dependency to see if there's a
+ * non-colocation requiremet on the given resource. Returns 1 if found,
+ * 0 if not.
+ */
+static int
+_tree_walk_noncolo(dep_t *dep, dep_t *start, char *name, dep_rs_t *sl,
+ int slen)
+{
+ dep_node_t *dn;
+ dep_rs_t *state;
+ int errors = 0, a;
+
+ if (dep == start)
+ return 0;
+
+ if (!dep->d_nodes)
+ return 0;
+
+ /* Start if this is the first call*/
+ if (start == NULL)
+ start = dep;
+
+ state = _find_state(dep->d_name, sl, slen);
+ if (!state)
+ return 0;
+
+ list_for(&dep->d_nodes, dn, a) {
+
+ /* If we have a specified requirement... */
+ if (dn->dn_colo == DEP_COLO_NEVER) {
+
+ if (!strcasecmp(dn->dn_name, name) &&
+ !(dn->dn_flags & DN_BROKEN_COLO)) {
+ /* Broken noncolo req */
+ state->rs_flags |= RS_BROKEN;
+ dn->dn_flags |= DN_BROKEN_NONCOLO;
+ ++errors;
+ }
+ }
+ }
+
+ return errors;
+}
+
+
+int
+dep_check_colo(dep_t **deps, dep_rs_t *states, int slen,
+ char *name, int nodeid)
+{
+ dep_t *dep;
+ int x, errors = 0;
+
+ if (!deps)
+ return 0;
+ dep = find_dep(deps, name);
+ if (!dep)
+ return 0;
+
+ for (x = 0; x < slen; x++) {
+
+ if (!rs_running(NULL, &states[x], 1)) {
+ /*
+ * Non-running resources don't cause colo/noncolo
+ * conflicts
+ */
+ continue;
+ }
+
+ /*
+ * Two resources on different nodes... see if the specified
+ * one is causing a colocation conflict (e.g. another one
+ * must colocate with it)
+ */
+ if (states[x].rs_status.rs_owner != nodeid) {
+ errors += _tree_walk_colo(dep, NULL,
+ states[x].rs_status.rs_name, states,
+ slen);
+ }
+
+ /*
+ * two resources on the same node... see if the specified one
+ * is causing a colocation conflict (e.g. another one must
+ * NOT colocate with it)
+ */
+ if (states[x].rs_status.rs_owner == nodeid) {
+ errors += _tree_walk_noncolo(dep, NULL,
+ states[x].rs_status.rs_name,
+ states, slen);
+ }
+ }
+
+ dep_clear_traversed(deps);
+
+ return errors;
+}
+
+
+/**
+ * Check to see if this resource is causing a colocation conflict. It can
+ * not cause if it is stopped. If it is running, it can cause a colocation
+ * conflict if something must not colocate with it.
+ */
+static int
+_dep_check_colo(dep_t **deps, dep_rs_t *states, int slen, dep_rs_t *state)
+{
+ if (!rs_running(NULL, state, 1)) {
+ /* not running : cannot be causing a colo conflict */
+ return 0;
+ }
+
+ return dep_check_colo(deps,
+ states,
+ slen,
+ state->rs_status.rs_name,
+ state->rs_status.rs_owner);
+}
+
+
+int
+dep_check_online(dep_rs_t *rs, int *nodes, int nlen)
+{
+ int x;
+
+ if (!rs_running(NULL, rs, 1)) {
+ return 0;
+ }
+ for (x = 0; x < nlen; x++){
+ if (rs->rs_status.rs_owner == nodes[x])
+ return 0;
+ }
+
+ rs->rs_flags |= RS_DEAD_NODE;
+ return 1;
+}
+
+
+/**
+ * Validate the current state of the cluster. This traverses the dependency
+ * graph looking for conflicts, as well as obvious errors (e.g. resource
+ * outside failover domain constraint, for example.
+ *
+ * @return -1 for invalid, 0 for ideal, or the # of services
+ * which need to be started to become ideal.
+ */
+int
+dep_check(dep_t **deps, dep_rs_t *states, int slen, int *nodes, int nlen)
+{
+ int x, ret = 0, errors = 0;
+
+ dep_clear_dep_errors(deps);
+ dep_clear_state_errors(states, slen);
+
+ for (x = 0; x < slen; x++) {
+
+ if (states[x].rs_status.rs_state == RG_STATE_STOPPED)
+ ++ret;
+
+ /*
+ * Pass 1: Check for services started on dead nodes
+ */
+ errors += dep_check_online(&states[x], nodes, nlen);
+
+ /*
+ * Pass 2: Check obvious legal-targets for resources
+ * (failover domain)
+ */
+ errors += dep_check_res_loc(&states[x]);
+
+ /*
+ * Pass 3: Check to see if this resource has all of its a
+ * resource dependencies satisfied.
+ */
+ errors += _dep_check_requires(deps, &states[x], states, slen);
+
+ /*
+ * Pass 4: Check to see if this resource is causing a
+ * colocation conflict (e.g. is cohabiting with a resource
+ * which must run apart from it)
+ */
+ errors += _dep_check_colo(deps, states, slen, &states[x]);
+ }
+
+ if (errors)
+ return -errors;
+
+ return ret;
+}
+
+
+int
+dep_cluster_state_dot(FILE *fp, dep_t **deps, dep_rs_t *states, int slen,
+ int *nodes, int nlen)
+{
+ int x, y, z, tmp, off = 0;
+ char str[64];
+
+ fprintf(fp, "digraph G {\n");
+
+ /* Print out node states */
+ for (x = 0; x < nlen; x++) {
+ fprintf(fp, "\tsubgraph cluster_%d {\n", x);
+ fprintf(fp, "\t\tlabel=node%d;\n", nodes[x]);
+ fprintf(fp, "\t\tstyle=filled;\n");
+ fprintf(fp, "\t\tcolor=lightgrey;\n");
+ fprintf(fp, "\t\tfontcolor=black;\n");
+ fprintf(fp, "\t\tnode [style=filled, color=lightgrey, "
+ "fontcolor=lightgrey];\n");
+
+ z = 0;
+
+ for (y = 0; y < slen; y++) {
+ if (rs_running(NULL, &states[y], 1) &&
+ states[y].rs_status.rs_owner == nodes[x]) {
+ tmp = 0;
+ if (!states[y].rs_allowed_len)
+ tmp = 1;
+ else {
+ for (z = 0; z<states[y].rs_allowed_len;
+ z++) {
+ if (states[y].rs_allowed[z] ==
+ states[y].rs_status.rs_owner) {
+ tmp = 1;
+ break;
+ }
+ }
+ }
+
+ ++z;
+
+ if (!tmp) {
+ fprintf(fp, "\t\tnode [style=filled, "
+ "color=\"#ff6060\", "
+ "shape=box, "
+ "fontcolor=black];\n");
+ } else {
+ fprintf(fp, "\t\tnode [style=filled, "
+ "color=white, shape=box, "
+ "fontcolor=black];\n");
+ }
+
+ strncpy(str, states[y].rs_status.rs_name,
+ sizeof(str));
+ _dot_clean_string(str);
+ fprintf(fp, "\t\t%s;\n", str);
+ }
+ }
+
+ if (!z) {
+ snprintf(str, sizeof(str), "node%d", x);
+ fprintf(fp, "\t\t%s;\n", str);
+ }
+ fprintf(fp, "\t}\n");
+ }
+
+ /* Show all 'started' on dead node resources in a red "node" */
+ off = 0;
+ for (x = 0; x < slen; x++) {
+
+ if (!(states[x].rs_flags & RS_DEAD_NODE))
+ continue;
+
+ if (!off) {
+ off = 1;
+ fprintf(fp, "\tsubgraph cluster_%d {\n", nlen+1);
+ fprintf(fp, "\t\tlabel=Dead;\n");
+ fprintf(fp, "\t\tstyle=filled;\n");
+ fprintf(fp, "\t\tcolor=\"#ff6060\";\n");
+ fprintf(fp, "\t\tfontcolor=black;\n");
+ fprintf(fp, "\t\tnode [color=white, style=filled, "
+ "shape=box, fontcolor=black];\n");
+ }
+
+ strncpy(str, states[x].rs_status.rs_name, sizeof(str));
+ _dot_clean_string(str);
+ fprintf(fp, "\t\t%s;\n", str);
+ }
+
+ if (off)
+ fprintf(fp, "\t}\n");
+
+ /* Show all 'stopped' in a 'stopped' "node" */
+ fprintf(fp, "\tsubgraph cluster_%d {\n", nlen+2);
+ fprintf(fp, "\t\tstyle=filled;\n");
+ fprintf(fp, "\t\tcolor=\"#60ff60\";\n");
+ fprintf(fp, "\t\tfontcolor=black;\n");
+ fprintf(fp, "\t\tlabel=Stopped;\n");
+ fprintf(fp, "\t\tnode [color=white, style=filled, shape=box, "
+ "fontcolor=black];\n");
+
+ z = 0;
+ for (x = 0; x < slen; x++) {
+
+ if (states[x].rs_status.rs_state != RG_STATE_STOPPED)
+ continue;
+
+ strncpy(str, states[x].rs_status.rs_name, sizeof(str));
+ _dot_clean_string(str);
+
+ ++z;
+ fprintf(fp, "\t\t%s;\n", str);
+ }
+
+ if (!z) {
+ fprintf(fp, "\t\tnode [style=filled, color=\"#60ff60\", "
+ "fontcolor=\"#60ff60\"];\n");
+ fprintf(fp, "\t\tStopped;\n");
+ }
+
+ fprintf(fp, "\t}\n");
+
+ /* Show all 'disabled' in a 'disabled' "node" */
+ fprintf(fp, "\tsubgraph cluster_%d {\n", nlen+3);
+ fprintf(fp, "\t\tlabel=Disabled;\n");
+ fprintf(fp, "\t\tstyle=filled;\n");
+ fprintf(fp, "\t\tcolor=\"#6060ff\";\n");
+ fprintf(fp, "\t\tfontcolor=black;\n");
+ fprintf(fp, "\t\tnode [color=white, style=filled, shape=box, "
+ "fontcolor=black];\n");
+
+ z = 0;
+ for (x = 0; x < slen; x++) {
+
+ if (states[x].rs_status.rs_state != RG_STATE_DISABLED)
+ continue;
+
+ ++z;
+ strncpy(str, states[x].rs_status.rs_name, sizeof(str));
+ _dot_clean_string(str);
+ fprintf(fp, "\t\t%s;\n", str);
+ }
+
+ if (!z) {
+ fprintf(fp, "\t\tnode [style=filled, color=\"#6060ff\", "
+ "fontcolor=\"#6060ff\"];\n");
+ fprintf(fp, "\t\tDisabled;\n");
+ }
+
+ fprintf(fp, "\t}\n");
+
+ _print_depends_dot(fp, deps);
+
+ fprintf(fp, "}\n");
+
+ return 0;
+}
+
+
+int
+dep_cluster_state(FILE *fp, dep_t **deps, dep_rs_t *states, int slen,
+ int *nodes, int nlen)
+{
+ int x, y, z, tmp, off = 0;
+
+ /* Print out node states */
+ for (x = 0; x < nlen; x++) {
+ fprintf(fp, "Node %d\n", nodes[x]);
+
+ for (y = 0; y < slen; y++) {
+ if (rs_running(NULL, &states[y], 1) &&
+ states[y].rs_status.rs_owner == nodes[x]) {
+ tmp = 0;
+ if (!states[y].rs_allowed_len)
+ tmp = 1;
+ else {
+ for (z = 0; z<states[y].rs_allowed_len;
+ z++) {
+ if (states[y].rs_allowed[z] ==
+ states[y].rs_status.rs_owner) {
+ tmp = 1;
+ break;
+ }
+ }
+ }
+
+ if (!tmp) {
+ fprintf(fp, "\t[ILLEGAL] ");
+ } else {
+ fprintf(fp, "\t");
+ }
+
+ fprintf(fp, "%s\n",
+ states[y].rs_status.rs_name);
+ }
+ }
+ fprintf(fp, "\n");
+ }
+
+ /* Show all 'started' on dead node resources in a red "node" */
+ off = 0;
+ for (x = 0; x < slen; x++) {
+
+ if (!(states[x].rs_flags & RS_DEAD_NODE))
+ continue;
+
+ if (!off) {
+ off = 1;
+ fprintf(fp, "Resources on dead nodes:\n");
+ }
+
+ fprintf(fp, "\t%s\n", states[x].rs_status.rs_name);
+ }
+
+ if (off)
+ fprintf(fp, "\n");
+
+ /* Show all 'stopped' in a 'stopped' "node" */
+ fprintf(fp, "Stopped resources:\n");
+
+ z = 0;
+ for (x = 0; x < slen; x++) {
+
+ if (states[x].rs_status.rs_state != RG_STATE_STOPPED)
+ continue;
+
+ fprintf(fp, "\t%s;\n", states[x].rs_status.rs_name);
+ }
+
+ fprintf(fp, "\n");
+
+ /* Show all 'disabled' */
+ fprintf(fp, "Disabled resources:\n");
+
+ z = 0;
+ for (x = 0; x < slen; x++) {
+
+ if (states[x].rs_status.rs_state != RG_STATE_DISABLED)
+ continue;
+
+ fprintf(fp, "\t%s;\n", states[x].rs_status.rs_name);
+ }
+
+ fprintf(fp, "\n");
+
+ return 0;
+}
+
+/**
+ Gets an attribute of a resource group.
+
+ @param res Resource
+ @param property Name of property to check for
+ @param ret Preallocated return buffer
+ @param len Length of buffer pointed to by ret
+ @return 0 on success, -1 on failure.
+ */
+int
+res_property(resource_t *res, char *property, char *ret, size_t len)
+{
+ int x = 0;
+
+ if (!res)
+ return -1;
+
+ for (; res->r_attrs[x].ra_name; x++) {
+ if (strcasecmp(res->r_attrs[x].ra_name, property))
+ continue;
+ strncpy(ret, res->r_attrs[x].ra_value, len);
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static void
+_set_allowed(dep_rs_t *state, fod_t *domain, int *nodes, int nlen)
+{
+ fod_node_t *fdn;
+ int cnt = 0, allowed = 0, x, y, z, tmp;
+
+ if (!domain) {
+ state->rs_allowed = malloc(sizeof(int)*nlen);
+ state->rs_allowed_len = nlen;
+
+ if (!state->rs_allowed)
+ return;
+ memcpy(state->rs_allowed, nodes, sizeof(int)*nlen);
+ return;
+ }
+
+ if (domain->fd_flags & FOD_RESTRICTED) {
+ list_for(&domain->fd_nodes, fdn, allowed);
+ } else {
+ allowed = nlen;
+ }
+
+ state->rs_allowed = malloc(sizeof(int)*allowed);
+ state->rs_allowed_len = allowed;
+ state->rs_flags = 0;
+
+ if (!state->rs_allowed)
+ return;
+
+ memset(state->rs_allowed, 0, sizeof(int)*allowed);
+
+ cnt = 0;
+ /* Failover domain prios are 1..100 */
+ if (!(domain->fd_flags & FOD_ORDERED)) {
+
+ /* Non-prio failover domain */
+ list_for(&domain->fd_nodes, fdn, x) {
+ state->rs_allowed[cnt++] = fdn->fdn_nodeid;
+ }
+ } else {
+ for (x = 0; x <= 100; x++) {
+ list_for(&domain->fd_nodes, fdn, y) {
+ if (fdn->fdn_prio != x)
+ continue;
+ state->rs_allowed[cnt++] = fdn->fdn_nodeid;
+ }
+ }
+
+ state->rs_flags |= RS_ORDERED;
+
+ if (!(domain->fd_flags & FOD_NOFAILBACK))
+ state->rs_flags |= RS_FAILBACK;
+ }
+
+ if (domain->fd_flags & FOD_RESTRICTED)
+ return;
+
+ /* allowed == nlen */
+ /* find missing nodes and fill them in */
+ for (x = 0; x < allowed; x++) {
+ if (state->rs_allowed[x] != 0)
+ continue;
+ for (y = 0; y < nlen; y++) {
+
+ tmp = 0;
+ for (z = 0; z < x; z++) {
+ if (nodes[y] == state->rs_allowed[z]) {
+ tmp = 1;
+ break;
+ }
+ }
+
+ if (!tmp) {
+ state->rs_allowed[x] = nodes[y];
+ }
+ }
+ }
+}
+
+
+dep_rs_t *
+dep_rstate_alloc(resource_node_t **restree, fod_t **domains, int *nodes,
+ int nlen, int *rs_cnt)
+{
+ dep_rs_t *rstates;
+ resource_node_t *rn;
+ int tl_res_count = 0, x;
+ char dom[64];
+ fod_t *fod;
+
+ list_for(restree, rn, tl_res_count);
+
+ rstates = malloc(sizeof(dep_rs_t) * tl_res_count);
+ if (!rstates)
+ return NULL;
+
+ memset(rstates, 0, (sizeof(dep_rs_t) * tl_res_count));
+
+ list_for(restree, rn, x) {
+ snprintf(rstates[x].rs_status.rs_name,
+ sizeof(rstates[x].rs_status.rs_name),
+ "%s:%s", rn->rn_resource->r_rule->rr_type,
+ rn->rn_resource->r_attrs[0].ra_value);
+
+ rstates[x].rs_status.rs_last_owner = 0;
+ rstates[x].rs_status.rs_owner = 0;
+ rstates[x].rs_status.rs_state = RG_STATE_STOPPED;
+
+ fod = NULL;
+ if (!res_property(rn->rn_resource, "domain", dom,
+ sizeof(dom))) {
+
+ fod = fod_find_domain(domains, dom);
+ }
+ _set_allowed(&rstates[x], fod, nodes, nlen);
+ }
+
+ *rs_cnt = tl_res_count;
+
+ return rstates;
+}
+
+
+void
+dep_rstate_free(dep_rs_t *states, int slen)
+{
+ int x;
+
+ if (!states)
+ return;
+ if (!slen)
+ return;
+
+ for (x = 0; x < slen; x++) {
+ if (states[x].rs_allowed && states[x].rs_allowed_len) {
+ free(states[x].rs_allowed);
+ states[x].rs_allowed = NULL;
+ states[x].rs_allowed_len = 0;
+ }
+ }
+
+ free(states);
+}
+
+
+dep_rs_t *
+dep_rstate_dup(dep_rs_t *states, int slen)
+{
+ dep_rs_t *states_new;
+ int x;
+
+ while ((states_new = malloc(sizeof(dep_rs_t) * slen)) == NULL)
+ usleep(10000);
+
+ memcpy(states_new, states, sizeof(dep_rs_t) * slen);
+
+ for (x = 0; x < slen; x++) {
+ if (!states[x].rs_allowed || !states[x].rs_allowed_len) {
+ states_new[x].rs_allowed = NULL;
+ continue;
+ }
+
+ while ((states_new[x].rs_allowed =
+ malloc(sizeof(int) * states[x].rs_allowed_len))==NULL)
+ usleep(10000);
+
+ memcpy(states_new[x].rs_allowed,
+ states[x].rs_allowed,
+ sizeof(int) * states[x].rs_allowed_len);
+ }
+
+ return states_new;
+}
+
+
+/**
+ * copy everything from src into dest except for the allowed pointer arrays
+ */
+void
+dep_rstate_copy(dep_rs_t *dest, dep_rs_t *src, int slen)
+{
+ int x, *ap;
+
+ for (x = 0; x < slen; x ++) {
+ ap = dest[x].rs_allowed;
+ memcpy(&dest[x], &src[x], sizeof(dep_rs_t));
+ dest[x].rs_allowed = ap;
+ }
+}
+
+
+static int
+dep_srt_cmp(dep_t *l, dep_rs_t *ls, dep_t *r, dep_rs_t *rs)
+{
+
+ if (ls->rs_status.rs_state == RG_STATE_STOPPED &&
+ rs->rs_status.rs_state != RG_STATE_STOPPED)
+ return 1;
+
+ if (ls->rs_status.rs_state != RG_STATE_STOPPED &&
+ rs->rs_status.rs_state == RG_STATE_STOPPED)
+ return -1;
+
+ if (ls->rs_status.rs_state != RG_STATE_STOPPED &&
+ rs->rs_status.rs_state != RG_STATE_STOPPED) {
+
+ if (!l && r)
+ return -1;
+ if (r && !l)
+ return 1;
+ if (!r && !l)
+ return 0;
+
+ /* running resource */
+ /*
+ * Left is greater if the hits exceed, or left's
+ * hits are zero
+ */
+ if (l->d_hits == 0 && r->d_hits != 0)
+ return -1;
+ if (l->d_hits != 0 && r->d_hits == 0)
+ return 1;
+
+ /* Otherwise, more hits, the farther away */
+ if (l->d_hits > r->d_hits)
+ return -1;
+ if (l->d_hits < r->d_hits)
+ return 1;
+ return 0;
+ }
+
+ /* both states are stopped */
+ if (!l && r)
+ return 1;
+ if (l && !r)
+ return -1;
+ if (!r && !l)
+ return 0;
+ if (l->d_deps > r->d_deps)
+ return -1;
+ if (l->d_deps < r->d_deps)
+ return 1;
+ if (l->d_hits > r->d_hits)
+ return -1;
+ if (l->d_hits < r->d_hits)
+ return 1;
+ return 0;
+}
+
+
+void
+dep_rstate_sort(dep_t **deps, dep_rs_t *states, int slen)
+{
+ dep_t *xd, *yd;
+ int x, y;
+ dep_rs_t tmp;
+
+ /* brute force sort */
+ for (x = 0; x < slen; x++) {
+ for (y = 0; y < x; y++) {
+ xd = find_dep(deps, states[x].rs_status.rs_name);
+ yd = find_dep(deps, states[y].rs_status.rs_name);
+
+ if (dep_srt_cmp(yd, &states[y], xd, &states[x]) < 0) {
+ memcpy(&tmp, &states[y], sizeof(dep_rs_t));
+ memcpy(&states[y], &states[x],
+ sizeof(dep_rs_t));
+ memcpy(&states[x], &tmp, sizeof(dep_rs_t));
+ }
+ }
+ }
+}
+
+
+static inline int
+_alter_state_start(dep_t **deps, dep_rs_t *state, int newowner)
+{
+ /* Try starting a resource */
+ if (state->rs_flags & RS_BEEN_STARTED)
+ return 0;
+
+ /* we just stopped this on this node */
+ if (state->rs_status.rs_last_owner == newowner)
+ return 0;
+
+ state->rs_status.rs_state = RG_STATE_STARTED;
+ state->rs_status.rs_owner = newowner;
+
+ /*
+ * Optimization: don't allow start+stop+start.
+ * of the same resource, unless it was in an
+ * error state.
+ */
+ state->rs_flags |= (RS_BEEN_STARTED | RS_BEEN_STOPPED);
+
+ return 1;
+}
+
+
+static inline int
+_alter_state_stop(dep_t **deps, dep_rs_t *state)
+{
+ dep_t *dep;
+ dep_node_t *dn;
+ int x;
+
+ if (state->rs_flags & RS_BEEN_STOPPED)
+ return 0;
+
+ dep = find_dep(deps, state->rs_status.rs_name);
+ if (!dep) {
+ printf("Dependency missing for %s\n",
+ state->rs_status.rs_name);
+ return 0;
+ }
+
+#if 0
+ if (dep->d_hits == 0) {
+ /* If nothing depends on this resource, stopping it is
+ * pointless
+ */
+ return 0;
+ }
+#endif
+
+ state->rs_status.rs_state = RG_STATE_STOPPED;
+ state->rs_status.rs_last_owner = state->rs_status.rs_owner;
+ state->rs_status.rs_owner = 0;
+ state->rs_flags |= RS_BEEN_STOPPED;
+
+ /* We can clear the state now. Set the last owner to nobody */
+ if (state->rs_flags & (RS_ILLEGAL_NODE |
+ RS_DEAD_NODE )) {
+ state->rs_status.rs_last_owner = 0;
+ state->rs_flags &= ~(RS_ILLEGAL_NODE | RS_DEAD_NODE);
+ return 1;
+ }
+
+ /*
+ * If we stopped it because of a broken dependency, then we can
+ * restart it on the same node.
+ */
+ list_for(&dep->d_nodes, dn, x) {
+ if (dn->dn_flags & (DN_BROKEN_COLO |
+ DN_BROKEN_REQ |
+ DN_BROKEN_NONCOLO)) {
+ state->rs_status.rs_last_owner = 0;
+ return 1;
+ }
+ }
+
+ return 1;
+}
+
+
+static inline int
+_alter_state(dep_t **deps, dep_rs_t *state, dep_op_t *op)
+{
+ if (!op) {
+ printf("No operation\n");
+ return 0;
+ }
+
+ if (op->do_op == RG_START)
+ return _alter_state_start(deps, state, op->do_nodeid);
+ return _alter_state_stop(deps, state);
+}
+
+
+static int *
+build_try_indices(struct _try *tries, int try_cnt, int idx_cnt)
+{
+ int *indices, x, y;
+ /* Build our indices according to score */
+
+ while ((indices = malloc(sizeof(int)*idx_cnt)) == 0)
+ usleep(10000);
+ y = 0;
+ for (x = 0; x < try_cnt; x++) {
+ if (!tries[x].ops)
+ continue;
+ indices[y] = x;
+ ++y;
+ }
+
+ return indices;
+}
+
+
+static void
+nuke_op_list(dep_op_t **ops)
+{
+ dep_op_t *op;
+
+ while ((op = *ops) != NULL) {
+ list_remove(ops, op);
+ free(op);
+ }
+}
+
+
+static void
+sort_try_indices(struct _try *tries, int *indices, int idx_cnt,
+ dep_rs_t *states, int slen)
+{
+ int x, y, z, swp;
+ dep_rs_t *cstate;
+
+ /*
+ * Got our list of indices into our 'tries' branch array,
+ * now sort. Brutish sort.. Really should take into account error
+ * states.; e.g. 0 1 2 3 4 5 -1 -2 -3...
+ */
+ for (x = 0; x < idx_cnt; x++) {
+ for (y = 0; y < x; y++) {
+
+ if (tries[indices[x]].score < tries[indices[y]].score){
+ swp = indices[x];
+ indices[x] = indices[y];
+ indices[y] = swp;
+ }
+
+ if (tries[indices[x]].score != tries[indices[y]].score)
+ continue;
+
+ /* See if we're ordered */
+ cstate = NULL;
+ for (z = 0; z < slen; z++) {
+ if (!strcmp(tries[indices[y]].ops->do_res,
+ states[z].rs_status.rs_name)) {
+ cstate = &states[z];
+ break;
+ }
+ }
+
+ if (!cstate || !(cstate->rs_flags & RS_ORDERED))
+ continue;
+
+ swp = 0;
+ for (z = 0; z < cstate->rs_allowed_len; z++) {
+ if (tries[indices[x]].ops->do_nodeid ==
+ cstate->rs_allowed[z]) {
+ swp = 1;
+ break;
+ } else if (tries[indices[y]].ops->do_nodeid ==
+ cstate->rs_allowed[z]) {
+ break;
+ }
+ }
+
+ if (!swp)
+ continue;
+
+ swp = indices[x];
+ indices[x] = indices[y];
+ indices[y] = swp;
+ }
+ }
+}
+
+
+static void
+free_try_list(struct _try *tries, int len)
+{
+ int x;
+
+ /* Clean up all remaining branch lists */
+ for (x = 0; x < len; x++) {
+ if (!tries[x].ops)
+ continue;
+ nuke_op_list(&(tries[x].ops));
+ }
+
+ /* Clean up branch list array */
+ free(tries);
+}
+
+
+static int
+check_online(int n, int *nodes, int nlen, int *nidx)
+{
+ for (*nidx = 0; *nidx < nlen; (*nidx)++)
+ if (n == nodes[*nidx])
+ return nodes[*nidx];
+ return -1;
+}
+
+
+static void
+insert_try(struct _try *tries, int idx, dep_op_t *op, int score, int iter)
+{
+ dep_op_t *newop;
+
+ while ((newop = malloc(sizeof(dep_op_t))) == NULL)
+ usleep(10000);
+ memcpy(newop, op, sizeof(dep_op_t));
+ newop->do_iter = iter;
+ list_insert(&(tries[idx].ops), newop);
+ tries[idx].score = score;
+}
+
+
+/**
+ * Calculate all of the possible branches from this point.
+ * Returns the number of possible branches.
+ */
+static int
+build_all_possible_tries(dep_t **deps, dep_rs_t *states_cpy, int slen,
+ int *nodes, int nlen, int start_score,
+ struct _try *tries, int iter, int depth)
+{
+ int x, y, nidx, err;
+ dep_rs_t tmp;
+ dep_op_t op;
+ int idx_cnt = 0;
+
+ for (x = 0; x < slen; x++) {
+
+ /* Immutable resources can't be moved */
+ if (states_cpy[x].rs_flags & RS_IMMUTABLE)
+ continue;
+
+ /* can't bother with non-running resources */
+ if (states_cpy[x].rs_status.rs_state == RG_STATE_DISABLED ||
+ states_cpy[x].rs_status.rs_state == RG_STATE_FAILED)
+ continue;
+
+ /*
+ * see if this set has already tried to manipulate this
+ * service
+ */
+ if ((states_cpy[x].rs_flags &
+ (RS_BEEN_STARTED|RS_BEEN_STOPPED)) ==
+ (RS_BEEN_STARTED|RS_BEEN_STOPPED))
+ continue;
+
+ /*
+ * Since we are operating on the same allowed list (and
+ * will not be freeing tmp), it's ok to just use memcpy here.
+ */
+ memcpy(&tmp, &states_cpy[x], sizeof(tmp));
+
+ op.do_op = RG_START; /* XXX the loop below must happen
+ at least once...*/
+
+ /* try for each nodeid that's a legal target */
+ for (y = 0; (op.do_op == RG_START) &&
+ y < states_cpy[x].rs_allowed_len; y++) {
+
+ memcpy(&states_cpy[x], &tmp, sizeof(tmp));
+ /*
+ * Find next node online in our allowed list
+ * nidx is the offset into the online nodes array
+ * where this node was found - since the online list
+ * of nodes might be smaller than the allowed list
+ * for the resource, we need to keep track of it for
+ * later.
+ *
+ * XXX Since we sort later in the alg., we can probably
+ * reverse these loops to get rid of nidx.
+ */
+ if ((err = check_online(states_cpy[x].rs_allowed[y],
+ nodes, nlen, &nidx)) < 0)
+ continue;
+
+ /* Reset flags, etc. */
+ memset(&op, 0, sizeof(dep_op_t));
+
+ if (rs_running(NULL, &states_cpy[x], 1)) {
+
+ /* Try stopping a resource */
+ op.do_op = RG_STOP;
+ strncpy(op.do_res,
+ states_cpy[x].rs_status.rs_name,
+ sizeof(op.do_res));
+ /*printf("stop %s %d %d %d ", op.do_res,
+ depth, iter, start_score);*/
+
+ } else if (states_cpy[x].rs_status.rs_state ==
+ RG_STATE_STOPPED &&
+ start_score >= 0) {
+
+ /* Try starting a resource. We can only
+ * do this after we clear the errors (e.g. stop)
+ * resources */
+ op.do_op = RG_START;
+ op.do_nodeid = err;
+ strncpy(op.do_res,
+ states_cpy[x].rs_status.rs_name,
+ sizeof(op.do_res));
+ /*printf("start %s %d %d %d ", op.do_res,
+ depth, iter, start_score);*/
+ } else {
+ /* Other states = can't do anything */
+ continue;
+ }
+
+ /*
+ * Try to apply our operation. If it was an invalid,
+ * operation, we just move on
+ */
+ if (_alter_state(deps, &states_cpy[x], &op) == 0)
+ continue;
+
+ /* Check each possible move at this level */
+ err = dep_check(deps, states_cpy, slen, nodes, nlen);
+ //printf(" [score: %d]\n", err);
+
+ /*
+ * Make sure the operation does not introduce an
+ * error
+ */
+ if ((start_score < 0 && err <= start_score) ||
+ (start_score >= 0 && err < 0)) {
+ /*
+ * Introduce new error = bad; drop this
+ * operation.
+ */
+ /*
+ printf("%s of %s introduced new err %d %d\n",
+ op.do_op==RG_START?"start":"stop",
+ op.do_res,
+ start_score, err);
+ */
+ continue;
+ }
+
+ /* No new error - this op is a valid thing to do */
+ /* Insert the op on to the try list and move on */
+ insert_try(tries, x * nlen + nidx, &op, err, iter);
+
+ /*
+ * Keep track of how many places we actually
+ * found a possible branch for returning
+ */
+ ++idx_cnt;
+ }
+ memcpy(&states_cpy[x], &tmp, sizeof(tmp));
+ }
+
+ return idx_cnt;
+}
+
+
+/**
+ * Breadth-first-search.
+ */
+static int
+_dep_calc_trans(dep_t **deps, dep_rs_t *states, int slen, int *nodes, int nlen,
+ int depth, int errors, dep_op_t **oplist, int *iter)
+{
+ int x, y, err;
+ struct _try *tries;
+ dep_rs_t *states_cpy;
+ dep_op_t *newop;
+ int best_idx = -1, best_score, start_score;
+ int *indices = NULL, idx_cnt = 0;
+
+ x = dep_check(deps, states, slen, nodes, nlen);
+ /* Ideal? */
+ if (x == 0)
+ return 0;
+ /* Introduced a new error? */
+ if ((*iter) && errors < 0 && x <= errors)
+ return x;
+
+ /* Went from sub-optimal to outright broken? */
+ if (errors > 0 && x < 0)
+ return x;
+
+ /*
+ * Impossible to try branch more than 2*slen times: we can only
+ * on the outside stop/start every resource in the cluster.
+ */
+ if (depth > (2*slen))
+ return errors;
+
+ /* Set up */
+ best_score = start_score = x;
+ ++(*iter);
+ /*
+ printf("[%d] start depth %d errors %d init %d\n",
+ *iter, depth, errors, start_score);
+ */
+
+ while ((tries = malloc(sizeof(*tries) * slen * nlen)) == NULL)
+ usleep(10000);
+ memset(tries, 0, sizeof(*tries) * slen * nlen);
+
+ /* Copy our states (don't alter parent's states) */
+ states_cpy = dep_rstate_dup(states, slen);
+
+ /* Find all possible things to try */
+ idx_cnt = build_all_possible_tries(deps, states_cpy, slen, nodes, nlen,
+ start_score, tries, *iter, depth);
+
+ /* Build our indices into an array */
+ indices = build_try_indices(tries, nlen*slen, idx_cnt);
+
+ /* Sort array indices */
+ sort_try_indices(tries, indices, idx_cnt, states_cpy, slen);
+
+ /*
+ * Now, do a recurse on the tree in order of best-score-first...
+ * (that's why we sorted above)
+ */
+ dep_rstate_copy(states_cpy, states, slen);
+ for (x = 0; x < idx_cnt; x++) {
+
+ /* state index = tries_index/node count len */
+ y = indices[x] / nlen;
+
+ /* Back this up for later */
+ dep_rstate_copy(&states_cpy[y], &states[y], 1);
+
+ /* Flip our state. */
+ if (_alter_state(deps, &states_cpy[y],
+ tries[indices[x]].ops) == 0) {
+ /* :( */
+ printf("Error: Logic error\n");
+ continue;
+ }
+
+ /* Recurse */
+ err = _dep_calc_trans(deps, states_cpy, slen, nodes, nlen,
+ depth + 1, start_score,
+ &(tries[indices[x]].ops), iter);
+
+ //dep_rstate_copy(&states_cpy[y], &states[y], 1);
+
+ /* Store the score for branching on this operation */
+ tries[indices[x]].score = err;
+
+ /* ... and the depth. XXX not done yet; someday, we should
+ * select the shortest path...
+ tries[indices[x]].depth = 0;
+ list_for_count(&tries[indices[x]].ops, newop,
+ tries[indices[x]].depth);
+ */
+
+ /* Keep track of our best score */
+ if ((best_score < 0 && err > best_score) ||
+ (best_score >= 0 && err >= 0 && err < best_score)) {
+ best_idx = indices[x];
+ best_score = err;
+ }
+ }
+
+ /* Free our index array; we don't need it anymore */
+ free(indices);
+
+ /* We don't need our temporary space anymore */
+ dep_rstate_free(states_cpy, slen);
+
+ if ((errors < 0 && best_score < errors) ||
+ (errors >= 0 && best_score > errors)) {
+ /* No good branch to take from here */
+ best_idx = -1;
+ best_score = start_score;
+ }
+
+ /* Append the so-called best list on to our passed-in list of
+ * operations (this goes on tries[...] above) */
+ if (best_idx != -1) {
+ while ((newop = tries[best_idx].ops) != NULL) {
+ list_remove(&(tries[best_idx].ops), newop);
+ list_insert(oplist, newop);
+ }
+
+ /* Get the best score */
+ best_score = tries[best_idx].score;
+ }
+
+ free_try_list(tries, nlen * slen);
+
+ /*
+ printf("[%d] end start score: %d, bestscore: %d depth: %d\n", *iter,
+ start_score, best_score, depth);
+ */
+
+ return best_score;
+}
+
+
+int
+dep_calc_trans(dep_t **deps, dep_rs_t *_states, int slen,
+ int *nodes, int nlen, dep_op_t **op_list, int *iter)
+{
+ int x, my_i, ops = 0, err = 0;
+ dep_op_t *newop;
+ dep_rs_t *states = NULL;
+
+ if (iter)
+ *iter = 0;
+ else {
+ my_i = 0;
+ iter = &my_i;
+ }
+
+ x = dep_check(deps, _states, slen, nodes, nlen);
+ if (x == 0)
+ return 0;
+
+ states = dep_rstate_dup(_states, slen);
+ for (x = 0; x < slen; x++) {
+ if (states[x].rs_flags & (RS_DEAD_NODE|RS_ILLEGAL_NODE)) {
+ states[x].rs_status.rs_state = RG_STATE_STOPPED;
+ while ((newop = malloc(sizeof(*newop))) == NULL)
+ sleep(1);
+ memset(newop, 0, sizeof(*newop));
+ newop->do_op = RG_STOP;
+ strncpy(newop->do_res, states[x].rs_status.rs_name,
+ sizeof(newop->do_res));
+ list_insert(op_list, newop);
+ ++ops;
+ }
+ }
+
+ /*
+ * Sort:
+ * low...
+ * (a) stopped resources which are not depended on (starting these
+ * will not affect any other resources)
+ * (b) stopped resources which are depended on - where the # of
+ * deps is increasing
+ * (c) started resources w/ deps, ordered from lowest to highest
+ * (d) started resources w/ no deps (transitioning these will have
+ * -no- effect.)
+ * ... high
+ */
+ dep_rstate_sort(deps, states, slen);
+ err = dep_check(deps, states, slen, nodes, nlen);
+ if (err) {
+ err = _dep_calc_trans(deps, states, slen, nodes, nlen, 0,
+ err, op_list, iter);
+ }
+
+ dep_rstate_free(states, slen);
+ return err;
+}
+
+
+int
+dep_apply_trans(dep_t **deps, dep_rs_t *states, int slen, dep_op_t **op_list)
+{
+ dep_op_t *op;
+ int ops = 0, a, x;
+
+ list_for(op_list, op, ops) {
+ for (x = 0; x < slen; x++) {
+ if (strcasecmp(op->do_res, states[x].rs_status.rs_name))
+ continue;
+ if (op->do_op == RG_START) {
+ printf("Start %s on %d [%d]\n",
+ op->do_res, op->do_nodeid, op->do_iter);
+ states[x].rs_status.rs_state = RG_STATE_STARTED;
+ states[x].rs_status.rs_owner = op->do_nodeid;
+ } else {
+ printf("Stop %s [%d]\n", op->do_res,
+ op->do_iter);
+ states[x].rs_status.rs_state = RG_STATE_STOPPED;
+ states[x].rs_status.rs_owner = 0;
+ states[x].rs_flags &= ~(RS_ILLEGAL_NODE|
+ RS_DEAD_NODE);
+ }
+ }
+ }
+
+ printf("Applied %d operations\n", ops);
+
+ return 0;
+}
+
+
+void
+reverse_list(dep_op_t **oplist)
+{
+ dep_op_t *new_ol = NULL;
+ dep_op_t *op;
+ int x, found;
+
+ if (!*oplist)
+ return;
+
+ while ((op = *oplist)) {
+ list_remove(oplist, op);
+ list_prepend(&new_ol, op);
+ }
+
+ *oplist = new_ol;
+}
+
+
+void
+insert_after_stops(dep_op_t **oplist, dep_op_t *newop)
+{
+ dep_op_t *new_ol = NULL;
+ dep_op_t *op;
+ int x, found;
+
+ if (!*oplist)
+ return;
+
+ while ((op = *oplist) && op->do_op == RG_STOP) {
+ list_remove(oplist, op);
+ list_insert(&new_ol, op);
+ }
+
+ list_insert(&new_ol, newop);
+
+ while ((op = *oplist)) {
+ list_remove(oplist, op);
+ list_insert(&new_ol, op);
+ }
+
+ *oplist = new_ol;
+}
+
+
+/**
+ * Generate the list of operations which would satisfy a requested (user)
+ * operation (e.g. start, relocate, disable)
+ */
+int
+dep_check_operation(char *res, int operation, int target,
+ dep_t **deps, dep_rs_t *_states,
+ int slen, int *nodes, int nlen, dep_op_t **oplist)
+{
+ int x, ret = -1;
+ dep_rs_t *state = NULL, *states = NULL;
+ dep_op_t *newop = NULL;
+ int start_score, score;
+
+ states = dep_rstate_dup(_states, slen);
+ start_score = dep_check(deps, _states, slen, nodes, nlen);
+
+ /* Find the state dealing with */
+ if (!(state = _find_state(res, states, slen))) {
+ printf("No record of that service...\n");
+ dep_rstate_free(states, slen);
+ return -1;
+ }
+
+ switch(operation) {
+ case RG_DISABLE:
+ case RG_STOP:
+ case RG_START:
+ while ((newop = malloc(sizeof(dep_op_t))) == NULL)
+ usleep(10000);
+ memset(newop, 0, sizeof(dep_op_t));
+
+ /* Set it up */
+ strncpy(newop->do_res, res, sizeof(newop->do_res));
+ newop->do_op = operation;
+ newop->do_nodeid = target;
+
+ if (_alter_state(deps, state, newop) == 0)
+ break;
+
+ state->rs_flags |= RS_IMMUTABLE;
+
+ start_score = dep_check(deps, states, slen, nodes, nlen);
+ score = dep_calc_trans(deps, states, slen, nodes, nlen,
+ oplist, NULL);
+ if (start_score < 0 && score <= start_score)
+ break;
+
+ /* Reverse the list if we breake dependencies */
+ if (operation == RG_STOP || operation == RG_DISABLE) {
+ /* Append the operation */
+ reverse_list(oplist);
+ list_insert(oplist, newop);
+ } else {
+ /* append after stops... */
+ insert_after_stops(oplist, newop);
+ }
+
+ ret = 0; /* Woot */
+ break;
+ case RG_RELOCATE:
+ if (dep_check_operation(res, RG_STOP, -1, deps, states, slen, nodes,
+ nlen, oplist) < 0)
+ break;
+ if (dep_check_operation(res, RG_START, target, deps, states, slen,
+ nodes, nlen, oplist) < 0)
+ break;
+ ret = 0; /* Woot */
+ break;
+ case RG_MIGRATE:
+ default:
+ break;
+ }
+
+ /* Ret will be -1 unless we succeeded */
+ if (ret < 0) {
+ if (newop)
+ free(newop);
+
+ while ((newop = *oplist)) {
+ list_remove(oplist, newop);
+ free(newop);
+ }
+ }
+
+ if (states)
+ dep_rstate_free(states, slen);
+ return ret;
+}
/cvs/cluster/cluster/rgmanager/src/daemons/dtest.c,v --> standard output
revision 1.1
--- cluster/rgmanager/src/daemons/dtest.c
+++ - 2007-02-20 19:56:19.276481000 +0000
@@ -0,0 +1,811 @@
+#include <libxml/parser.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/xpath.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <resgroup.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <list.h>
+#include <reslist.h>
+#include <pthread.h>
+#include <depends.h>
+#include <ccs.h>
+#include <readline/readline.h>
+#include <readline/history.h>
+
+
+
+#ifndef NO_CCS
+#error "Can not be built with CCS support."
+#endif
+
+#ifdef NO_CCS
+#define ccs_get(fd, query, ret) conf_get(query, ret)
+#endif
+
+
+resource_rule_t *rules = NULL;
+resource_t *resources = NULL;
+resource_node_t *restree = NULL;
+dep_t *depends = NULL;
+dep_rs_t *resource_states = NULL;
+int resource_state_count = 0;
+fod_t *domains = NULL;
+int *nodes_all = NULL;
+int nodes_all_count = 0;
+int *nodes_online = NULL;
+int nodes_online_count = 0;
+
+
+void
+visualize_state(char *png_viewer)
+{
+ char cmd[1024];
+ char tf_dot[128];
+ char tf_png[128];
+ FILE *fp;
+ int fd, x;
+
+ snprintf(tf_dot, sizeof(tf_dot), "/tmp/dtest.dot.XXXXXX");
+ fd = mkstemp(tf_dot);
+ if (fd < 0) {
+ printf("Couldn't create temporary file: %s\n", strerror(errno));
+ return;
+ }
+
+ fp = fdopen(fd, "w+");
+ if (!fp) {
+ printf("Couldn't init temporary file: %s\n", strerror(errno));
+ return;
+ }
+
+ x = dep_check(&depends, resource_states,
+ resource_state_count, nodes_online,
+ nodes_online_count);
+ printf("State score: %d\n", x);
+ dep_cluster_state_dot(fp, &depends, resource_states,
+ resource_state_count, nodes_online,
+ nodes_online_count);
+
+ fclose(fp);
+ close(fd);
+
+ snprintf(tf_png, sizeof(tf_png), "/tmp/dtest.png.XXXXXX");
+ fd = mkstemp(tf_png);
+ if (fd < 0) {
+ printf("Couldn't init temporary file: %s\n", strerror(errno));
+ return;
+ }
+
+ close(fd);
+
+ snprintf(cmd, sizeof(cmd), "dot -Tpng -o %s %s", tf_png, tf_dot);
+ if (system(cmd) != 0) {
+ printf("Error: system('%s') failed\n", cmd);
+ return;
+ }
+
+ snprintf(cmd, sizeof(cmd), "%s %s", png_viewer, tf_png);
+ if (system(cmd) != 0) {
+ printf("Error: system('%s') failed\n", cmd);
+ return;
+ }
+
+ unlink(tf_png);
+ unlink(tf_dot);
+}
+
+
+int
+vis_dep_apply_trans(dep_op_t **op_list, char *pngviewer)
+{
+ dep_op_t *op;
+ int x;
+ int ops = 0;
+ dep_rs_t *states = resource_states;
+ int slen = resource_state_count;
+
+ visualize_state(pngviewer);
+
+ list_do(op_list, op) {
+
+ ++ops;
+
+ for (x = 0; x < slen; x++) {
+ if (strcasecmp(op->do_res, states[x].rs_status.rs_name))
+ continue;
+ if (op->do_op == RG_START) {
+ printf("Start %s on %d\n", op->do_res, op->do_nodeid);
+ states[x].rs_status.rs_state = RG_STATE_STARTED;
+ states[x].rs_status.rs_owner = op->do_nodeid;
+ } else {
+ printf("Stop %s\n", op->do_res);
+ states[x].rs_status.rs_state = RG_STATE_STOPPED;
+ states[x].rs_status.rs_owner = 0;
+ states[x].rs_flags &= ~(RS_ILLEGAL_NODE|
+ RS_DEAD_NODE);
+ }
+ break;
+ }
+
+ visualize_state(pngviewer);
+
+ } while (!list_done(op_list, op));
+
+ printf("Applied %d operations\n", ops);
+
+ return 0;
+}
+
+void
+print_help(void)
+{
+ printf("nodes view all nodes\n");
+ printf("online [id1 id2...|none] set or view online nodes\n");
+ printf("res [resource] view a resource state (or all)\n");
+ printf("start <res> <id> start res on id\n");
+ printf("dep [res] print dependency tree(s)\n");
+ printf("stop <res1> [res2...] stop res\n");
+ printf("disable <res1> [res2...] disable res\n");
+ printf("check check cluster state against deps\n");
+ printf("calc calculate transition to valid state\n");
+ printf("apply [pngviewer] Apply previous transition list\n");
+ printf(" If pngviewer is specified, send\n");
+ printf(" graphviz & bring up viewer\n");
+ printf("state [pngviewer] dump cluster state in DOT format\n");
+ printf(" If pngviewer is specified, send\n");
+ printf(" graphviz & bring up viewer\n");
+ printf("quit, exit exit\n");
+}
+
+
+void
+show_rs_t(dep_rs_t *rs)
+{
+ int *allowed, cnt, x;
+
+ printf("Resource %s\n State: %s\n Owner: %d\n",
+ rs->rs_status.rs_name,
+ rg_state_str(rs->rs_status.rs_state),
+ rs->rs_status.rs_owner);
+
+ if (rs->rs_allowed) {
+ allowed = rs->rs_allowed;
+ cnt = rs->rs_allowed_len;
+ } else {
+ printf(" Allowed Nodes = [ all ]\n");
+ return;
+ }
+
+ printf(" Allowed Nodes = [ ");
+
+ for (x = 0; x < cnt; x++) {
+ printf("%d ", allowed[x]);
+ }
+ printf("]\n");
+}
+
+
+int
+show_resources(char *name)
+{
+ int x, found = 0;
+
+ for (x = 0; x < resource_state_count; x++) {
+ if (!name || !strcmp(name,
+ resource_states[x].rs_status.rs_name)) {
+ found = 1;
+
+ show_rs_t(&resource_states[x]);
+ }
+
+ if (!name)
+ printf("\n");
+ }
+
+ return !found;
+}
+
+
+void
+show_calc_result(int final_score, dep_op_t **ops, int iter)
+{
+ int x = 0;
+ dep_op_t *op;
+
+ list_do(ops, op) {
+ ++x;
+ if (op->do_op == RG_START) {
+ printf("Start %s on %d [%d]\n", op->do_res, op->do_nodeid, op->do_iter);
+ } else {
+ printf("Stop %s [%d]\n", op->do_res, op->do_iter);
+ }
+ } while (!list_done(ops, op));
+
+ if (iter >= 0)
+ printf("%d operations reduced in %d iterations; final score = %d\n", x, iter,
+ final_score);
+ else
+ printf("%d operations\n", x);
+}
+
+
+
+dep_rs_t *
+get_rs_byname(char *name)
+{
+ int x, found = 0;
+
+ if (!name)
+ return NULL;
+
+ for (x = 0; x < resource_state_count; x++) {
+ if (!name || !strcmp(name,
+ resource_states[x].rs_status.rs_name)) {
+ found = 1;
+
+ return &resource_states[x];
+ }
+
+ }
+
+ return NULL;
+}
+
+
+
+void
+dtest_shell(void)
+{
+ resource_t *res;
+ char name[64];
+ char *cmd = NULL;
+ int done = 0;
+ char *save, *curr, *tmp;
+ int cnt, err;
+ int *nodes;
+ int x;
+ dep_rs_t *rs;
+ dep_t *depcpy;
+ dep_op_t *ops = NULL, *op;
+
+ nodes = malloc(sizeof(int)*nodes_all_count);
+
+ while (!done) {
+
+ if (cmd)
+ free(cmd);
+ cmd = readline("> ");
+ if (!cmd || !strlen(cmd)) {
+ printf("\n");
+ }
+ if (!cmd) {
+ break;
+ }
+
+ /*
+ if (cmd && cmd[0])
+ add_history(cmd);
+ */
+
+ if (!cmd[0])
+ continue;
+
+ curr = strtok_r(cmd, " ", &save);
+ err = 0;
+
+ if (!strcmp(curr, "online")) {
+ cnt = 0;
+ err = 0;
+ while ((curr = strtok_r(NULL, " ", &save))) {
+ nodes[cnt] = atoi(curr);
+ if (nodes[cnt] <= 0) {
+ printf("Error: Node '%s' invalid\n",
+ curr);
+ err = 1;
+ break;
+ }
+
+ err = 1;
+ for (x = 0; x < nodes_all_count; x++) {
+ if (nodes_all[x] == nodes[cnt]) {
+ err = 0;
+ break;
+ }
+ }
+ ++cnt;
+ }
+
+ if (cnt && !err) {
+ if (nodes_online)
+ free(nodes_online);
+
+ nodes_online = malloc(sizeof(int) * cnt);
+
+ for (x = 0; x < cnt; x++)
+ nodes_online[x] = nodes[x];
+ nodes_online_count = cnt;
+ }
+
+ if (!err) {
+ printf("Online = [ ");
+ for (x = 0; x < nodes_online_count; x++) {
+ printf("%d ", nodes_online[x]);
+ }
+ printf("]\n");
+ }
+ } else if (!strcmp(curr, "start")) {
+
+ curr = strtok_r(NULL, " ", &save);
+
+ if (!curr) {
+ printf("usage: start <resource> <nodeid>"
+ " [test]\n");
+ continue;
+ }
+
+ if (!strchr(curr,':')) {
+ snprintf(name, sizeof(name), "service:%s",
+ curr);
+ curr = name;
+ }
+
+ rs = get_rs_byname(curr);
+ if (!rs) {
+ printf("Error: Resource '%s' not found\n", curr);
+ ++err;
+ }
+
+ curr = strtok_r(NULL, " ", &save);
+ cnt = 0;
+ if (!curr) {
+ printf("Error: No node ID specified\n");
+ ++err;
+ } else {
+ cnt = atoi(curr);
+ x = 0;
+ if (cnt <= 0) {
+ printf("Error: Node '%s' invalid\n",
+ curr);
+ ++err;
+ } else {
+ for (x = 0; x < nodes_online_count; x++) {
+ if (nodes_online[x] == cnt) {
+ x = -1;
+ break;
+ }
+ }
+ }
+
+ if (x != -1) {
+ printf("Error: Node '%s' not online\n",
+ curr);
+ ++err;
+ }
+ }
+
+ if (err)
+ continue;
+
+ curr = strtok_r(NULL, " ", &save);
+ if (curr) {
+ if (strcmp(curr, "test"))
+ printf("Error: start ... %s\n", curr);
+
+ while ((op = ops)) {
+ list_remove(&ops, op);
+ free(op);
+ }
+ /*
+int
+dep_check_operation(char *res, int operation, int target,
+ dep_t **deps, dep_rs_t *_states,
+ int slen, int *nodes, int nlen, dep_op_t **oplist)
+ */
+ if (dep_check_operation(rs->rs_status.rs_name,
+ RG_START,
+ cnt,
+ &depends,
+ resource_states,
+ resource_state_count,
+ nodes_online,
+ nodes_online_count,
+ &ops) < 0) {
+ printf("No, thanks.\n");
+ } else
+ show_calc_result(0, &ops, 0);
+ continue;
+ }
+
+
+ rs->rs_status.rs_owner = cnt;
+ rs->rs_status.rs_state = RG_STATE_STARTED;
+ printf("%s is started on %d\n", rs->rs_status.rs_name,
+ cnt);
+
+ } else if (!strcmp(curr, "domains")) {
+ print_domains(&domains);
+
+ } else if (!strcmp(curr, "calc")) {
+
+ while ((op = ops)) {
+ list_remove(&ops, op);
+ free(op);
+ }
+
+ err = dep_calc_trans(&depends, resource_states,
+ resource_state_count,
+ nodes_online, nodes_online_count,
+ &ops, &x);
+ show_calc_result(err, &ops, x);
+
+ } else if (!strcmp(curr, "apply")) {
+
+ curr = strtok_r(NULL, " ", &save);
+ dep_check(&depends, resource_states,
+ resource_state_count, nodes_online,
+ nodes_online_count);
+ if (!curr) {
+ dep_apply_trans(&depends, resource_states,
+ resource_state_count,
+ &ops);
+ } else {
+ vis_dep_apply_trans(&ops, curr);
+ }
+
+ while ((op = ops)) {
+ list_remove(&ops, op);
+ free(op);
+ }
+ } else if (!strcmp(curr, "state")) {
+
+ x = dep_check(&depends, resource_states,
+ resource_state_count,
+ nodes_online, nodes_online_count);
+
+ if (x < 0) {
+ printf("Cluster state is invalid (%d errors)\n",
+ -x);
+ dep_print_errors(&depends,
+ resource_states,
+ resource_state_count);
+ } else if (x > 0) {
+ printf("Cluster state is valid, "
+ "but not ideal (%d stopped)\n",x);
+ } else {
+ printf("Cluster state is ideal\n");
+ }
+
+ curr = strtok_r(NULL, " ", &save);
+ if (!curr) {
+ dep_cluster_state(stdout, &depends,
+ resource_states,
+ resource_state_count, nodes_online,
+ nodes_online_count);
+ } else {
+ visualize_state(curr);
+ }
+
+ dep_reset(&depends, resource_states,
+ resource_state_count);
+
+ } else if (!strcmp(curr, "reslist")) {
+ list_do(&resources, res) {
+ print_resource(res);
+ } while (!list_done(&resources, res));
+
+ } else if (!strcmp(curr, "nodes")) {
+
+ printf("Nodes = [ ");
+ for (x = 0; x < nodes_all_count; x++) {
+ printf("%d ", nodes_all[x]);
+ }
+ printf("]\n");
+ } else if (!strcmp(curr, "stop") || !strcmp(curr, "disable")) {
+
+ tmp = curr;
+
+ curr = strtok_r(NULL, " ", &save);
+
+ if (!curr) {
+ printf("usage: %s <resource>\n", tmp);
+ continue;
+ }
+ #if 0
+ do {
+ if (!strchr(curr,':')) {
+ snprintf(name, sizeof(name), "service:%s",
+ curr);
+ curr = name;
+ }
+ rs = get_rs_byname(curr);
+ if (!rs) {
+ printf("Error: Resource '%s' not found\n",curr);
+ break;
+ }
+
+ rs->rs_status.rs_owner = 0;
+ if (!strcmp(cmd, "stop")) {
+ rs->rs_status.rs_state = RG_STATE_STOPPED;
+ printf("%s is stopped\n",
+ rs->rs_status.rs_name);
+ } else {
+ rs->rs_status.rs_state = RG_STATE_DISABLED;
+ printf("%s is disabled\n",
+ rs->rs_status.rs_name);
+ }
+ curr = strtok_r(NULL, " ", &save);
+ } while (curr);
+
+ curr = strtok_r(NULL, " ", &save);
+
+ if (!curr) {
+ printf("usage: start <resource> <nodeid>"
+ " [test]\n");
+ continue;
+ }
+ #endif
+
+ if (!strchr(curr,':')) {
+ snprintf(name, sizeof(name), "service:%s",
+ curr);
+ curr = name;
+ }
+
+ rs = get_rs_byname(curr);
+ if (!rs) {
+ printf("Error: Resource '%s' not found\n", curr);
+ ++err;
+ }
+
+ if (err)
+ continue;
+
+ curr = strtok_r(NULL, " ", &save);
+ if (curr) {
+ if (strcmp(curr, "test"))
+ printf("Error: stop ... %s\n", curr);
+
+ while ((op = ops)) {
+ list_remove(&ops, op);
+ free(op);
+ }
+
+ if(dep_check_operation(rs->rs_status.rs_name,
+ !strcmp(tmp,"stop")?RG_STOP:RG_DISABLE,
+ -1,
+ &depends,
+ resource_states,
+ resource_state_count,
+ nodes_online,
+ nodes_online_count,
+ &ops) < 0) {
+ printf("No.\n");
+ } else
+ show_calc_result(0, &ops, 0);
+ continue;
+ }
+
+ rs->rs_status.rs_owner = 0;
+ if (!strcmp(cmd, "stop")) {
+ rs->rs_status.rs_state = RG_STATE_STOPPED;
+ printf("%s is stopped\n",
+ rs->rs_status.rs_name);
+ } else {
+ rs->rs_status.rs_state = RG_STATE_DISABLED;
+ printf("%s is disabled\n",
+ rs->rs_status.rs_name);
+ }
+
+
+ } else if (!strcmp(curr, "res")) {
+
+ curr = strtok_r(NULL, " ", &save);
+
+ if (curr && !strchr(curr,':')) {
+ snprintf(name, sizeof(name), "service:%s",
+ curr);
+ curr = name;
+ }
+ err = show_resources(curr);
+ if (err) {
+ printf("Error: Invalid resource '%s'", curr);
+ }
+
+ } else if (!strcmp(curr, "dep")) {
+
+ curr = strtok_r(NULL, " ", &save);
+
+ if (!curr) {
+ print_depends(stdout, &depends);
+ continue;
+ }
+
+ if (!strcmp(curr, "dot")) {
+ print_depends_dot(stdout, &depends);
+ continue;
+ } else if (!strcmp(curr, "copy")) {
+ printf("Copying tree...");
+ depcpy = NULL;
+ dep_tree_dup(&depcpy, &depends);
+ printf("Done\n");
+ print_depends(stdout, &depcpy);
+ deconstruct_depends(&depcpy);
+ depcpy = NULL;
+ } else {
+ printf("Error: Invalid command 'dep %s'\n",
+ curr);
+ }
+
+ } else if (!strcmp(curr, "check")) {
+ dep_reset(&depends, resource_states,
+ resource_state_count);
+
+ x = dep_check(&depends, resource_states,
+ resource_state_count,
+ nodes_online, nodes_online_count);
+
+ if (x < 0) {
+ printf("Cluster state is invalid (%d errors)\n",
+ -x);
+ dep_print_errors(&depends,
+ resource_states,
+ resource_state_count);
+ dep_reset(&depends, resource_states,
+ resource_state_count);
+ } else if (x > 0) {
+ printf("Cluster state is valid, "
+ "but not ideal (%d stopped)\n",x);
+ } else {
+ printf("Cluster state is ideal\n");
+ }
+ } else if (!strcmp(curr, "?") || !strcmp(curr,"help")) {
+ print_help();
+ } else if (!strcmp(curr, "quit") || !strcmp(curr,"exit")) {
+ done = 1;
+ } else if (!strcmp(curr, "mem")) {
+
+ tmp = curr;
+ curr = strtok_r(NULL, " ", &save);
+ if (!curr) {
+ malloc_stats();
+ continue;
+ }
+
+ if (!strcmp(curr, "table")) {
+ malloc_dump_table();
+ } else {
+ printf("Unknown command '%s %s'\n", tmp , curr);
+ }
+ } else {
+ printf("Unknown command '%s'\n", curr);
+ }
+ }
+ if (cmd)
+ free(cmd);
+}
+
+
+int *
+load_node_ids(int ccsfd, int *count)
+{
+ int ncount, x;
+ int *nodes;
+ char *val;
+ char xpath[256];
+
+ for (ncount = 1; ; ncount++) {
+ snprintf(xpath, sizeof(xpath),
+ "/cluster/clusternodes/clusternode[%d]/@nodeid",
+ ncount);
+
+ if (ccs_get(ccsfd, xpath, &val)!=0) {
+ --ncount;
+ break;
+ }
+ }
+
+ if (!ncount)
+ return NULL;
+
+ nodes = malloc(sizeof(int) * ncount);
+ if (!nodes) {
+ fprintf(stderr, "out of memory?\n");
+ return NULL;
+ }
+
+ for (x = 1; x <= ncount; x++) {
+ snprintf(xpath, sizeof(xpath),
+ "/cluster/clusternodes/clusternode[%d]/@nodeid", x);
+
+ if (ccs_get(ccsfd, xpath, &val)!=0) {
+ fprintf(stderr,
+ "Code path error: # of nodes changed\n");
+ free(nodes);
+ return NULL;
+ }
+
+ nodes[x-1] = atoi(val);
+ free(val);
+ }
+
+ *count = ncount;
+ return nodes;
+}
+
+
+int
+main(int argc, char **argv)
+{
+ int ccsfd, ret = 0;
+ char *agentpath;
+ char *config;
+
+ if (argc < 3) {
+ printf("usage: %s <agentpath> <config>\n", argv[0]);
+ return -1;
+ }
+
+ agentpath = argv[1];
+ config = argv[2];
+
+ conf_setconfig(config);
+
+ ccsfd = ccs_lock();
+ if (ccsfd < 0) {
+ printf("Error parsing %s\n", argv[1]);
+ return -1;
+ }
+
+ load_resource_rules(agentpath, &rules);
+ construct_domains(ccsfd, &domains);
+ load_resources(ccsfd, &resources, &rules);
+ build_resource_tree(ccsfd, &restree, &rules, &resources);
+ construct_depends(ccsfd, &depends);
+
+ if (argc >= 4) {
+ if (!strcmp(argv[3], "dot")) {
+ print_depends_dot(stdout, &depends);
+ } else {
+ fprintf(stderr,"Invalid command: %s\n", argv[3]);
+ ret = 1;
+ }
+ goto out;
+ }
+
+ nodes_all = load_node_ids(ccsfd, &nodes_all_count);
+
+ ccs_unlock(ccsfd);
+
+ printf("Nodes = [ ");
+ for (ret = 0; ret < nodes_all_count; ret++) {
+ printf("%d ", nodes_all[ret]);
+ }
+ printf("]\n");
+
+ if ((resource_states = dep_rstate_alloc(&restree, &domains,
+ nodes_all,
+ nodes_all_count,
+ &resource_state_count)) == NULL) {
+ printf("oops\n");
+ return -1;
+ }
+
+ /* Ok! We have it all! */
+ dtest_shell();
+
+out:
+
+ deconstruct_depends(&depends);
+ destroy_resource_tree(&restree);
+ destroy_resources(&resources);
+ deconstruct_domains(&domains);
+ destroy_resource_rules(&rules);
+
+ malloc_dump_table();
+
+ return ret;
+}
+
+
--- cluster/rgmanager/src/daemons/Makefile 2007/01/26 21:57:01 1.15
+++ cluster/rgmanager/src/daemons/Makefile 2007/02/20 19:56:18 1.16
@@ -41,7 +41,8 @@
clurgmgrd: rg_thread.o rg_locks.o main.o groups.o \
rg_queue.o rg_forward.o reslist.o \
resrules.o restree.o fo_domain.o nodeevent.o \
- rg_event.o watchdog.o rg_state.o ../clulib/libclulib.a
+ rg_event.o watchdog.o rg_state.o \
+ depends.o ../clulib/libclulib.a
$(CC) -o $@ $^ $(INCLUDE) $(CFLAGS) $(LDFLAGS) -lccs -lcman -lpthread -ldlm
#
@@ -59,8 +60,13 @@
# packages should run 'make check' as part of the build process.
#
rg_test: rg_locks-noccs.o test-noccs.o reslist-noccs.o \
- resrules-noccs.o restree-noccs.o fo_domain-noccs.o
+ resrules-noccs.o restree-noccs.o fo_domain-noccs.o depends-noccs.o
$(CC) -o $@ $^ $(INCLUDE) $(CFLAGS) -llalloc $(LDFLAGS) -lccs -lcman
+
+dtest: rg_locks-noccs.o dtest-noccs.o reslist-noccs.o \
+ resrules.o restree-noccs.o fo_domain-noccs.o depends-noccs.o
+ $(CC) -o $@ $^ $(INCLUDE) $(CFLAGS) -llalloc $(LDFLAGS) -lccs -lcman \
+ -lreadline -ltermcap
clurmtabd: clurmtabd.o clurmtabd_lib.o
$(CC) -o $@ $^ $(INCLUDE) $(CFLAGS) $(LDFLAGS)
--- cluster/rgmanager/src/daemons/test.c 2006/07/19 18:43:32 1.6
+++ cluster/rgmanager/src/daemons/test.c 2007/02/20 19:56:18 1.7
@@ -27,6 +27,7 @@
#include <list.h>
#include <reslist.h>
#include <pthread.h>
+#include <depends.h>
#ifndef NO_CCS
#error "Can not be built with CCS support."
@@ -78,9 +79,36 @@
int
+deps_func(int argc, char**argv)
+{
+ dep_t *depends = NULL;
+ int ccsfd;
+
+ conf_setconfig(argv[1]);
+ ccsfd = ccs_lock();
+ if (ccsfd < 0) {
+ printf("Error parsing %s\n", argv[1]);
+ goto out;
+ }
+
+ construct_depends(ccsfd, &depends);
+ if (depends) {
+ print_depends(stdout, &depends);
+ }
+
+ deconstruct_depends(&depends);
+
+out:
+ ccs_unlock(ccsfd);
+ return 0;
+}
+
+
+int
test_func(int argc, char **argv)
{
fod_t *domains = NULL;
+ dep_t *depends = NULL;
resource_rule_t *rulelist = NULL, *currule;
resource_t *reslist = NULL, *curres;
resource_node_t *tree = NULL;
@@ -97,6 +125,7 @@
load_resource_rules(agentpath, &rulelist);
construct_domains(ccsfd, &domains);
+ construct_depends(ccsfd, &depends);
load_resources(ccsfd, &reslist, &rulelist);
build_resource_tree(ccsfd, &tree, &rulelist, &reslist);
@@ -131,6 +160,11 @@
printf("=== Failover Domains ===\n");
print_domains(&domains);
}
+
+ if (depends) {
+ printf("=== Dependencies ===\n");
+ print_depends(stdout, &depends);
+ }
}
ccs_unlock(ccsfd);
@@ -177,6 +211,7 @@
}
out:
+ deconstruct_depends(&depends);
deconstruct_domains(&domains);
destroy_resource_tree(&tree);
destroy_resources(&reslist);
@@ -244,8 +279,6 @@
print_resource(curres);
} while (!list_done(&reslist2, curres));
- curres = find_root_by_ref(&reslist, "oracle");
-
resource_tree_delta(&tree, &tree2);
printf("=== Old Resource Tree ===\n");
print_resource_tree(&tree);
@@ -295,6 +328,10 @@
shift();
ret = test_func(argc, argv);
goto out;
+ } else if (!strcmp(argv[1], "depends")) {
+ shift();
+ ret = deps_func(argc, argv);
+ goto out;
} else if (!strcmp(argv[1], "rules")) {
shift();
ret = rules_func(argc, argv);
More information about the Cluster-devel
mailing list