All of lore.kernel.org
 help / color / mirror / Atom feed
* [Cluster-devel] cluster/rgmanager/src/daemons Makefile test.c  ...
@ 2007-02-20 19:56 lhh
  0 siblings, 0 replies; only message in thread
From: lhh @ 2007-02-20 19:56 UTC (permalink / raw)
  To: cluster-devel.redhat.com

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
+		 		       @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);



^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2007-02-20 19:56 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-02-20 19:56 [Cluster-devel] cluster/rgmanager/src/daemons Makefile test.c lhh

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.