diff --git a/TODO.markdown b/TODO.markdown
index 9bc8f859c2..f7dd8e1eda 100644
--- a/TODO.markdown
+++ b/TODO.markdown
@@ -1,57 +1,55 @@
 # Semi-random collection of tasks we'd like to get done
 
 ## Targeted for 1.2
 - Move inline function bodies out of headers
   - peer2text, level2char, lrmd_event_type2str, lrmd_event_rc2str, 
 - Move location of cib.xml to somewhere more appropriate
 - Avoid the use of xmlNode in fencing register_callback() call types
 - Need a way to indicate when unfencing operations need to be initiated from the host to be unfenced
 
 ## Targeted for 1.2.x
 
 - Allow the N in 'give up after N failed fencing attempts' to be configurable 
 - Log cib:diff in compressed form (only the ++ and -- entries)  
 - Check for uppercase letters in node names, warn if found
 - Imply startup-failure-is-fatal from on-fail="restart" 
 - Show an english version of the config with crm_resource --rules
 - Convert cts/CIB.py into a supported Python API for the CIB
 - Use crm_log_tag() in the PE to allow per-resource trace logging
 - Reduce the amount of stonith-ng logging
 - Use dlopen for snmp in crm_mon
 - Re-implement no-quorum filter for cib updates?
 
 ## Targeted for 1.4
 
 - Support A colocated with (B || C || D)
 - Implement a truely atomic version of attrd
 - Support rolling average values in attrd
 - Support heartbeat with the mcp
 - Freeze/Thaw
 - Create Pacemaker plugin for snmpd - http://www.net-snmp.org/
 - Investigate using a DB as the back-end for the CIB
 - Decide whether to fully support or drop failover domains
 
 # Testing
 - Create a BSC with all the regression tests
 - Convert BandwidthTest CTS test into a Scenario wrapper
 - find_operations() is not covered by PE regression tests
 - Some node states in determine_online_status_fencing() are untested by PE regression tests
 - no_quorum_policy==suicide is not covered by PE regression tests
 - parse_xml_duration() is not covered by PE regression tests
 - phase_of_the_moon() is not covered by PE regression tests
 - test_role_expression() is not covered by PE regression tests
 - native_parameter() is not covered by PE regression tests
-- clone_resource_state() is not covered by PE regression tests
 - clone_active() is not covered by PE regression tests
 - convert_non_atomic_task() in native.c is not covered by PE regression tests
-- clone_rsc_colocation_lh() is not covered by PE regression tests
 - group_rsc_colocation_lh() is not covered by PE regression tests
 - Test on-fail=standby
 
 # Documentation
 - Clusters from Scratch: Mail
 - Clusters from Scratch: MySQL
 - Document reload in Pacemaker Explained
 - Document advanced fencing logic in Pacemaker Explained
 - Use ann:defaultValue="..." instead of <optional> in the schema more often
 - Allow Clusters from Scratch to be built in two flavors - pcs and crm shell
diff --git a/fencing/regression.py.in b/fencing/regression.py.in
index ea508b7c96..6df180abc8 100644
--- a/fencing/regression.py.in
+++ b/fencing/regression.py.in
@@ -1,496 +1,545 @@
 #!/usr/bin/python
 
 #
 # 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
 # of the License, 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; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
 
 
 import os
 import sys
 import subprocess
 import shlex
 import time
 
 def output_from_command(command):
 	test = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 	test.wait()
 
 	return test.communicate()[0].split("\n")
 
 class Test:
 	def __init__(self, name, description, verbose = 0, with_cpg = 0):
 		self.name = name
 		self.description = description
 		self.cmds = []
 		self.verbose = verbose
 
 		self.result_txt = ""
 		self.cmd_tool_output = ""
 		self.result_exitcode = 0;
 
 		self.stonith_options = "-s"
 		self.enable_corosync = 0
 		if with_cpg:
 			self.stonith_options = "-c"
 			self.enable_corosync = 1
 
 		self.stonith_process = None
 		self.stonith_output = ""
 		self.stonith_patterns = []
+		self.negative_stonith_patterns = []
 
 		self.executed = 0
 
 		rsc_classes = output_from_command("crm_resource --list-standards")
 
 		self.has_systemd = 0
 		if "systemd" in rsc_classes:
 			self.has_systemd = 1
 
 	def __new_cmd(self, cmd, args, exitcode, stdout_match = "", no_wait = 0, stdout_negative_match = "", kill=None):
 		self.cmds.append(
 			{
 				"cmd" : cmd,
 				"kill" : kill,
 				"args" : args,
 				"expected_exitcode" : exitcode,
 				"stdout_match" : stdout_match,
 				"stdout_negative_match" : stdout_negative_match,
 				"no_wait" : no_wait,
 			}
 		)
 
 	def start_corosync(self):
 		if self.enable_corosync == 0:
 			return
 
 		if self.has_systemd:
 			cmd = shlex.split("systemctl start corosync.service")
 		else:
 			cmd = shlex.split("service corosync start")
 
 		test = subprocess.Popen(cmd, stdout=subprocess.PIPE)
 		test.wait()
 
 	def stop_corosync(self):
 		if self.enable_corosync == 0:
 			return
 
 		if self.has_systemd:
 			cmd = shlex.split("systemctl stop corosync.service")
 		else:
 			cmd = shlex.split("service corosync stop")
 		test = subprocess.Popen(cmd, stdout=subprocess.PIPE)
 		test.wait()
 
 	def stop_pacemaker(self):
 		if self.has_systemd:
 			cmd = shlex.split("systemctl stop pacemaker.service")
 		else:
 			cmd = shlex.split("service pacemaker stop")
 		test = subprocess.Popen(cmd, stdout=subprocess.PIPE)
 		test.wait()
 
 	def start_environment(self):
 		### make sure we are in full control here ###
 		self.stop_pacemaker()
 		self.stop_corosync()
 
 		cmd = shlex.split("killall -q -9 stonithd")
 		test = subprocess.Popen(cmd, stdout=subprocess.PIPE)
 		test.wait()
 
 		self.start_corosync()
 
 		self.stonith_process = subprocess.Popen(
 			shlex.split("@CRM_DAEMON_DIR@/stonithd %s -V" % self.stonith_options),
 			stdout=subprocess.PIPE,
 			stderr=subprocess.PIPE)
 
 		time.sleep(1)
 
 	def clean_environment(self):
 		if self.stonith_process:
-			self.stonith_process.kill()
+			self.stonith_process.terminate()
 
 		self.stonith_output = self.stonith_process.communicate()[1]
 		self.stonith_process = None
 
 		self.stop_corosync()
 
 	def add_stonith_log_pattern(self, pattern):
 		self.stonith_patterns.append(pattern)
 
+	def add_stonith_negative_log_pattern(self, pattern):
+		self.negative_stonith_patterns.append(pattern)
+
 	def add_cmd(self, cmd, args):
 		self.__new_cmd(cmd, args, 0, "")
 
 	def add_cmd_no_wait(self, cmd, args):
 		self.__new_cmd(cmd, args, 0, "", 1)
 
 	def add_cmd_check_stdout(self, cmd, args, match, no_match = ""):
 		self.__new_cmd(cmd, args, 0, match, 0, no_match)
 
 	def add_expected_fail_cmd(self, cmd, args, exitcode = 255):
 		self.__new_cmd(cmd, args, exitcode, "")
 
 	def get_exitcode(self):
 		return self.result_exitcode
 
 	def print_result(self, filler):
 		print "%s%s" % (filler, self.result_txt)
 
 	def run_cmd(self, args):
 		cmd = shlex.split(args['args'])
 		cmd.insert(0, args['cmd'])
 
 		if self.verbose:
 			print "\n\nRunning: "+" ".join(cmd)
 		test = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 
 		if args['kill']:
 			if self.verbose:
 				print "Also running: "+args['kill']
 			subprocess.Popen(shlex.split(args['kill']))
 
 		if args['no_wait'] == 0:
 			test.wait()
 		else:
 			return 0
 
 		output = test.communicate()[0]
 
 		if args['stdout_match'] != "" and output.count(args['stdout_match']) == 0:
 			test.returncode = -2
 			print "STDOUT string '%s' was not found in cmd output: %s" % (args['stdout_match'], output)
 
 		if args['stdout_negative_match'] != "" and output.count(args['stdout_negative_match']) != 0:
 			test.returncode = -2
 			print "STDOUT string '%s' was found in cmd output: %s" % (args['stdout_negative_match'], output)
 
 		return test.returncode;
 
+
+	def count_negative_matches(self, outline):
+		count = 0
+		for line in self.negative_stonith_patterns:
+			if outline.count(line):
+				count = 1
+				if self.verbose:
+					print "This pattern should not have matched = '%s" % (line)
+		return count
+
 	def match_stonith_patterns(self):
+		negative_matches = 0
 		cur = 0
 		total_patterns = len(self.stonith_patterns)
 
 		if len(self.stonith_patterns) == 0:
 			return
 
 		for line in self.stonith_output.split("\n"):
+			negative_matches = negative_matches + self.count_negative_matches(line)
+			if cur == total_patterns:
+				continue
 			if line.count(self.stonith_patterns[cur]):
 				cur = cur + 1
-			if cur == total_patterns:
-				break
 
-		if cur != len(self.stonith_patterns):
+		if cur != len(self.stonith_patterns) or negative_matches:
 			for item in range(total_patterns):
 				if self.verbose and item > (cur -1):
 					print "Pattern Not Matched = '%s'" % self.stonith_patterns[item]
 
-			self.result_txt = "FAILURE - '%s' failed. %d patterns out of %d not matched" % (self.name, total_patterns - cur, total_patterns)
+			self.result_txt = "FAILURE - '%s' failed. %d patterns out of %d not matched. %d negative matches." % (self.name, total_patterns - cur, total_patterns, negative_matches)
 			self.result_exitcode = -1
 
 	def run(self):
 		res = 0
 		i = 1
 		self.start_environment()
 
 		if self.verbose:
 			print "\n--- START TEST - %s" % self.name
 
 		self.result_txt = "SUCCESS - '%s'" % (self.name)
 		self.result_exitcode = 0
 		for cmd in self.cmds:
 			res = self.run_cmd(cmd)
 			if res != cmd['expected_exitcode']:
 				print "Step %d FAILED - command returned %d, expected %d" % (i, res, cmd['expected_exitcode'])
 				self.result_txt = "FAILURE - '%s' failed at step %d. Command: lrmd_test %s" % (self.name, i, cmd['args'])
 				self.result_exitcode = -1
 				break
 			else:
 				if self.verbose:
 					print "Step %d SUCCESS" % (i)
 			i = i + 1
 		self.clean_environment()
 
 		if self.result_exitcode == 0:
 			self.match_stonith_patterns()
 
 		print self.result_txt
 		if self.verbose:
 			print "--- END TEST - %s\n" % self.name
 
 		self.executed = 1
 		return res
 
 class Tests:
 	def __init__(self, verbose = 0):
 		self.tests = []
 		self.verbose = verbose
 
 	def new_test(self, name, description, with_cpg = 0):
 		test = Test(name, description, self.verbose, with_cpg)
 		self.tests.append(test)
 		return test
 
 	def print_list(self):
 		print "\n==== %d TESTS FOUND ====" % (len(self.tests))
 		print "%35s - %s" % ("TEST NAME", "TEST DESCRIPTION")
 		print "%35s - %s" % ("--------------------", "--------------------")
 		for test in self.tests:
 			print "%35s - %s" % (test.name, test.description)
 		print "==== END OF LIST ====\n"
 
 	def run_single(self, name):
 		for test in self.tests:
 			if test.name == name:
 				test.run()
 				break;
 
 	def run_tests_matching(self, pattern):
 		for test in self.tests:
 			if test.name.count(pattern) != 0:
 				test.run()
 
 	def run_tests(self):
 		for test in self.tests:
 			test.run()
 
 	def print_results(self):
 		failures = 0;
 		success = 0;
 		print "\n\n======= FINAL RESULTS =========="
 		print "\n--- FAILURE RESULTS:"
 		for test in self.tests:
 			if test.executed == 0:
 				continue
 
 			if test.get_exitcode() != 0:
 				failures = failures + 1
 				test.print_result("    ")
 			else:
 				success = success + 1
 
 		if failures == 0:
 			print "    None"
 
 		print "\n--- TOTALS\n    Pass:%d\n    Fail:%d\n" % (success, failures)
 	def build_api_sanity_tests(self):
 		verbose_arg = ""
 		if self.verbose:
 			verbose_arg = "-V"
 
 		test = self.new_test("api_sanity_test", "Sanity test client api.")
 		test.add_cmd("@CRM_DAEMON_DIR@/stonith-test", "-t %s" % (verbose_arg))
 
 	def build_standalone_tests(self):
 		test_types = [
 			{
 				"prefix" : "standalone" ,
 				"use_cpg" : 0,
 			},
 			{
 				"prefix" : "cpg" ,
 				"use_cpg" : 1,
 			},
 		]
 
 		# test what happens when multiple devices can fence a node, but the first device fails.
 		for test_type in test_types:
 			test = self.new_test("%s_fence_device_failure" % test_type["prefix"],
 					"Verify that when one fence device fails for a node, the others are tried.", test_type["use_cpg"])
 			test.add_cmd("stonith_admin", "-R false1 -a fence_false -o \"pcmk_host_list=node1 node2 node3\"")
 			test.add_cmd("stonith_admin", "-R true1  -a fence_true -o \"pcmk_host_list=node1 node2 node3\"")
 			test.add_cmd("stonith_admin", "-R false2 -a fence_false -o \"pcmk_host_list=node1 node2 node3\"")
 			test.add_cmd("stonith_admin", "-F node3 -t 5")
 
 		# simple topology test for one device
 		for test_type in test_types:
 			if test_type["use_cpg"] == 0:
 				continue
 
 			test = self.new_test("%s_topology_simple" % test_type["prefix"],
 					"Verify all fencing devices at a level are used.", test_type["use_cpg"])
 			test.add_cmd("stonith_admin", "-R true  -a fence_true -o \"pcmk_host_list=node1 node2 node3\"")
 
 			test.add_cmd("stonith_admin", "-r node3 -i 1 -v true")
 			test.add_cmd("stonith_admin", "-F node3 -t 5")
 
 			test.add_stonith_log_pattern("for host 'node3' with device 'true' returned: 0")
 
 		# test what happens when the first fencing level has multiple devices.
 		for test_type in test_types:
 			if test_type["use_cpg"] == 0:
 				continue
 
 			test = self.new_test("%s_topology_device_fails" % test_type["prefix"],
 					"Verify if one device in a level fails, the other is tried.", test_type["use_cpg"])
 			test.add_cmd("stonith_admin", "-R false  -a fence_false -o \"pcmk_host_list=node1 node2 node3\"")
 			test.add_cmd("stonith_admin", "-R true  -a fence_true -o \"pcmk_host_list=node1 node2 node3\"")
 
 			test.add_cmd("stonith_admin", "-r node3 -i 1 -v false")
 			test.add_cmd("stonith_admin", "-r node3 -i 2 -v true")
 			test.add_cmd("stonith_admin", "-F node3 -t 5")
 
 			test.add_stonith_log_pattern("for host 'node3' with device 'false' returned: -1001")
 			test.add_stonith_log_pattern("for host 'node3' with device 'true' returned: 0")
 
 		# test what happens when the first fencing level fails.
 		for test_type in test_types:
 			if test_type["use_cpg"] == 0:
 				continue
 
 			test = self.new_test("%s_topology_multi_level_fails" % test_type["prefix"],
 					"Verify if one level fails, the next leve is tried.", test_type["use_cpg"])
 			test.add_cmd("stonith_admin", "-R true1  -a fence_true -o \"pcmk_host_list=node1 node2 node3\"")
 			test.add_cmd("stonith_admin", "-R true2  -a fence_true -o \"pcmk_host_list=node1 node2 node3\"")
 			test.add_cmd("stonith_admin", "-R true3  -a fence_true -o \"pcmk_host_list=node1 node2 node3\"")
 			test.add_cmd("stonith_admin", "-R true4  -a fence_true -o \"pcmk_host_list=node1 node2 node3\"")
 			test.add_cmd("stonith_admin", "-R false1 -a fence_false -o \"pcmk_host_list=node1 node2 node3\"")
 			test.add_cmd("stonith_admin", "-R false2 -a fence_false -o \"pcmk_host_list=node1 node2 node3\"")
 
 			test.add_cmd("stonith_admin", "-r node3 -i 1 -v false1")
 			test.add_cmd("stonith_admin", "-r node3 -i 1 -v true1")
 			test.add_cmd("stonith_admin", "-r node3 -i 2 -v true2")
 			test.add_cmd("stonith_admin", "-r node3 -i 2 -v false2")
 			test.add_cmd("stonith_admin", "-r node3 -i 3 -v true3")
 			test.add_cmd("stonith_admin", "-r node3 -i 3 -v true4")
 
 			test.add_cmd("stonith_admin", "-F node3 -t 5")
 
 
 			test.add_stonith_log_pattern("for host 'node3' with device 'false1' returned: -1001")
 			test.add_stonith_log_pattern("for host 'node3' with device 'false2' returned: -1001")
 			test.add_stonith_log_pattern("for host 'node3' with device 'true3' returned: 0")
 			test.add_stonith_log_pattern("for host 'node3' with device 'true4' returned: 0")
 
+		# Test what happens if multiple fencing levels are defined, and then the first one is removed.
+		for test_type in test_types:
+			if test_type["use_cpg"] == 0:
+				continue
+
+			test = self.new_test("%s_topology_level_removal" % test_type["prefix"],
+					"Verify level removal works.", test_type["use_cpg"])
+			test.add_cmd("stonith_admin", "-R true1  -a fence_true -o \"pcmk_host_list=node1 node2 node3\"")
+			test.add_cmd("stonith_admin", "-R true2  -a fence_true -o \"pcmk_host_list=node1 node2 node3\"")
+			test.add_cmd("stonith_admin", "-R true3  -a fence_true -o \"pcmk_host_list=node1 node2 node3\"")
+			test.add_cmd("stonith_admin", "-R true4  -a fence_true -o \"pcmk_host_list=node1 node2 node3\"")
+			test.add_cmd("stonith_admin", "-R false1 -a fence_false -o \"pcmk_host_list=node1 node2 node3\"")
+			test.add_cmd("stonith_admin", "-R false2 -a fence_false -o \"pcmk_host_list=node1 node2 node3\"")
+
+			test.add_cmd("stonith_admin", "-r node3 -i 1 -v false1")
+			test.add_cmd("stonith_admin", "-r node3 -i 1 -v true1")
+
+			test.add_cmd("stonith_admin", "-r node3 -i 2 -v true2")
+			test.add_cmd("stonith_admin", "-r node3 -i 2 -v false2")
+
+			test.add_cmd("stonith_admin", "-r node3 -i 3 -v true3")
+			test.add_cmd("stonith_admin", "-r node3 -i 3 -v true4")
+
+			# Now remove level 2, verify none of the devices in level two are hit.
+			test.add_cmd("stonith_admin", "-d node3 -i 2")
+
+			test.add_cmd("stonith_admin", "-F node3 -t 5")
+
+			test.add_stonith_log_pattern("for host 'node3' with device 'false1' returned: -1001")
+			test.add_stonith_negative_log_pattern("for host 'node3' with device 'false2' returned: -1001")
+			test.add_stonith_log_pattern("for host 'node3' with device 'true3' returned: 0")
+			test.add_stonith_log_pattern("for host 'node3' with device 'true4' returned: 0")
+
 		# test the stonith builds the correct list of devices that can fence a node.
 		for test_type in test_types:
 			test = self.new_test("%s_list_devices" % test_type["prefix"],
 					"Verify list of devices that can fence a node is correct", test_type["use_cpg"])
 			test.add_cmd("stonith_admin", "-R true1  -a fence_true -o \"pcmk_host_list=node3\"")
 			test.add_cmd("stonith_admin", "-R true2 -a fence_true -o \"pcmk_host_list=node1 node2 node3\"")
 			test.add_cmd("stonith_admin", "-R true3 -a fence_true -o \"pcmk_host_list=node1 node2 node3\"")
 
 			test.add_cmd_check_stdout("stonith_admin", "-l node1 -V", "true2", "true1")
 			test.add_cmd_check_stdout("stonith_admin", "-l node1 -V", "true3", "true1")
 
 		# simple test of device monitor
 		for test_type in test_types:
 			test = self.new_test("%s_monitor" % test_type["prefix"],
 					"Verify device is reachable", test_type["use_cpg"])
 			test.add_cmd("stonith_admin", "-R true1  -a fence_true -o \"pcmk_host_list=node3\"")
 			test.add_cmd("stonith_admin", "-R false1  -a fence_false -o \"pcmk_host_list=node3\"")
 
 			test.add_cmd("stonith_admin", "-Q true1")
 			test.add_cmd("stonith_admin", "-Q false1")
 			test.add_expected_fail_cmd("stonith_admin", "-Q true2", 237)
 
 		# simple register test
 		for test_type in test_types:
 			test = self.new_test("%s_register" % test_type["prefix"],
 					"Verify devices can be registered and un-registered", test_type["use_cpg"])
 			test.add_cmd("stonith_admin", "-R true1  -a fence_true -o \"pcmk_host_list=node3\"")
 
 			test.add_cmd("stonith_admin", "-Q true1")
 
 			test.add_cmd("stonith_admin", "-D true1")
 
 			test.add_expected_fail_cmd("stonith_admin", "-Q true1", 237)
 
 		# test fencing history.
 		for test_type in test_types:
 			if test_type["use_cpg"] == 0:
 				continue
 			test = self.new_test("%s_fence_history" % test_type["prefix"],
 					"Verify last fencing operation is returned.", test_type["use_cpg"])
 			test.add_cmd("stonith_admin", "-R true1  -a fence_true -o \"pcmk_host_list=node3\"")
 
 			test.add_cmd("stonith_admin", "-F node3 -t 5 -V")
 
 			test.add_cmd_check_stdout("stonith_admin", "-H node3", "was able to turn off node node3", "")
 
 class TestOptions:
 	def __init__(self):
 		self.options = {}
 		self.options['list-tests'] = 0
 		self.options['run-all'] = 1
 		self.options['run-only'] = ""
 		self.options['run-only-pattern'] = ""
 		self.options['verbose'] = 0
 		self.options['invalid-arg'] = ""
 		self.options['show-usage'] = 0
 
 	def build_options(self, argv):
 		args = argv[1:]
 		skip = 0
 		for i in range(0, len(args)):
 			if skip:
 				skip = 0
 				continue
 			elif args[i] == "-h" or args[i] == "--help":
 				self.options['show-usage'] = 1
 			elif args[i] == "-l" or args[i] == "--list-tests":
 				self.options['list-tests'] = 1
 			elif args[i] == "-V" or args[i] == "--verbose":
 				self.options['verbose'] = 1
 			elif args[i] == "-r" or args[i] == "--run-only":
 				self.options['run-only'] = args[i+1]
 				skip = 1
 			elif args[i] == "-p" or args[i] == "--run-only-pattern":
 				self.options['run-only-pattern'] = args[i+1]
 				skip = 1
 
 	def show_usage(self):
 		print "usage: " + sys.argv[0] + " [options]"
 		print "If no options are provided, all tests will run"
 		print "Options:"
 		print "\t [--help | -h]                        Show usage"
 		print "\t [--list-tests | -l]                  Print out all registered tests."
 		print "\t [--run-only | -r 'testname']         Run a specific test"
 		print "\t [--verbose | -V]                     Verbose output"
 		print "\t [--run-only-pattern | -p 'string']   Run only tests containing the string value"
 		print "\n\tExample: Run only the test 'start_top'"
 		print "\t\t python ./regression.py --run-only start_stop"
 		print "\n\tExample: Run only the tests with the string 'systemd' present in them"
 		print "\t\t python ./regression.py --run-only-pattern systemd"
 
 def main(argv):
 	o = TestOptions()
 	o.build_options(argv)
 
 	tests = Tests(o.options['verbose'])
 	tests.build_standalone_tests()
 	tests.build_api_sanity_tests()
 
 	os.system("cp /usr/share/pacemaker/tests/cts/fence_false /usr/sbin/fence_false")
 	os.system("cp /usr/share/pacemaker/tests/cts/fence_true /usr/sbin/fence_true")
 
 	print "Starting ..."
 
 	if o.options['list-tests']:
 		tests.print_list()
 	elif o.options['show-usage']:
 		o.show_usage()
 	elif o.options['run-only-pattern'] != "":
 		tests.run_tests_matching(o.options['run-only-pattern'])
 		tests.print_results()
 	elif o.options['run-only'] != "":
 		tests.run_single(o.options['run-only'])
 		tests.print_results()
 	else:
 		tests.run_tests()
 		tests.print_results()
 
 if __name__=="__main__":
 	main(sys.argv)
diff --git a/lib/lrmd/lrmd_client.c b/lib/lrmd/lrmd_client.c
index 333488ce54..70530999e2 100644
--- a/lib/lrmd/lrmd_client.c
+++ b/lib/lrmd/lrmd_client.c
@@ -1,1012 +1,1015 @@
 /*
  * Copyright (c) 2012 David Vossel <dvossel@redhat.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2.1 of the License, or (at your option) any later version.
  * 
  * This library 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
  * Lesser General Public License for more details.
  * 
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  *
  */
 
 #include <crm_internal.h>
 
 #include <unistd.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <stdarg.h>
 #include <string.h>
 #include <ctype.h>
 
 #include <sys/types.h>
 #include <sys/wait.h>
 
 #include <glib.h>
 #include <dirent.h>
 
 #include <crm/crm.h>
 #include <crm/lrmd.h>
 #include <crm/services.h>
 #include <crm/common/mainloop.h>
 #include <crm/msg_xml.h>
 
 #include <crm/stonith-ng.h>
 
 CRM_TRACE_INIT_DATA(lrmd);
 
 static stonith_t *stonith_api = NULL;
 
 typedef struct lrmd_private_s {
     int call_id;
 
     char *token;
     crm_ipc_t *ipc;
     mainloop_io_t *source;
 
     lrmd_event_callback callback;
 
 } lrmd_private_t;
 
 static lrmd_list_t *
 lrmd_list_add(lrmd_list_t * head, const char *value)
 {
     lrmd_list_t *p, *end;
 
     p = calloc(1, sizeof(lrmd_list_t));
     p->val = strdup(value);
 
     end = head;
     while (end && end->next) {
         end = end->next;
     }
 
     if (end) {
         end->next = p;
     } else {
         head = p;
     }
 
     return head;
 }
 
 void
 lrmd_list_freeall(lrmd_list_t * head)
 {
     lrmd_list_t *p;
 
     while (head) {
         char *val = (char *)head->val;
 
         p = head->next;
         free(val);
         free(head);
         head = p;
     }
 }
 
 lrmd_key_value_t *
 lrmd_key_value_add(lrmd_key_value_t * head, const char *key, const char *value)
 {
     lrmd_key_value_t *p, *end;
 
     p = calloc(1, sizeof(lrmd_key_value_t));
     p->key = strdup(key);
     p->value = strdup(value);
 
     end = head;
     while (end && end->next) {
         end = end->next;
     }
 
     if (end) {
         end->next = p;
     } else {
         head = p;
     }
 
     return head;
 }
 
 static void
 lrmd_key_value_freeall(lrmd_key_value_t * head)
 {
     lrmd_key_value_t *p;
 
     while (head) {
         p = head->next;
         free(head->key);
         free(head->value);
         free(head);
         head = p;
     }
 }
 
 static void
 dup_attr(gpointer key, gpointer value, gpointer user_data)
 {
     g_hash_table_replace(user_data, strdup(key), strdup(value));
 }
 
 lrmd_event_data_t *
 lrmd_copy_event(lrmd_event_data_t * event)
 {
     lrmd_event_data_t *copy = NULL;
 
     copy = calloc(1, sizeof(lrmd_event_data_t));
 
     /* This will get all the int values.
      * we just have to be careful not to leave any
      * dangling pointers to strings. */
     memcpy(copy, event, sizeof(lrmd_event_data_t));
 
     copy->rsc_id = event->rsc_id ? strdup(event->rsc_id) : NULL;
     copy->op_type = event->op_type ? strdup(event->op_type) : NULL;
     copy->user_data = event->user_data ? strdup(event->user_data) : NULL;
     copy->output = event->output ? strdup(event->output) : NULL;
 
     if (event->params) {
         copy->params = g_hash_table_new_full(crm_str_hash,
                                              g_str_equal, g_hash_destroy_str, g_hash_destroy_str);
 
         if (copy->params != NULL) {
             g_hash_table_foreach(event->params, dup_attr, copy->params);
         }
     }
 
     return copy;
 }
 
 void
 lrmd_free_event(lrmd_event_data_t * event)
 {
     if (!event) {
         return;
     }
 
     /* free gives me grief if i try to cast */
     free((char *)event->rsc_id);
     free((char *)event->op_type);
     free((char *)event->user_data);
     free((char *)event->output);
     if (event->params) {
         g_hash_table_destroy(event->params);
     }
     free(event);
 }
 
 static int
 lrmd_dispatch_internal(const char *buffer, ssize_t length, gpointer userdata)
 {
     const char *type;
     lrmd_t *lrmd = userdata;
     lrmd_private_t *native = lrmd->private;
     lrmd_event_data_t event = { 0, };
     xmlNode *msg;
 
     if (!native->callback) {
         /* no callback set */
         return 1;
     }
 
     msg = string2xml(buffer);
     type = crm_element_value(msg, F_LRMD_OPERATION);
     crm_element_value_int(msg, F_LRMD_CALLID, &event.call_id);
     event.rsc_id = crm_element_value(msg, F_LRMD_RSC_ID);
 
     if (crm_str_eq(type, LRMD_OP_RSC_REG, TRUE)) {
         event.type = lrmd_event_register;
     } else if (crm_str_eq(type, LRMD_OP_RSC_UNREG, TRUE)) {
         event.type = lrmd_event_unregister;
     } else if (crm_str_eq(type, LRMD_OP_RSC_EXEC, TRUE)) {
         crm_element_value_int(msg, F_LRMD_TIMEOUT, &event.timeout);
         crm_element_value_int(msg, F_LRMD_RSC_INTERVAL, &event.interval);
         crm_element_value_int(msg, F_LRMD_RSC_START_DELAY, &event.start_delay);
         crm_element_value_int(msg, F_LRMD_EXEC_RC, (int *)&event.rc);
         crm_element_value_int(msg, F_LRMD_OP_STATUS, &event.op_status);
         crm_element_value_int(msg, F_LRMD_RSC_DELETED, &event.rsc_deleted);
 
         crm_element_value_int(msg, F_LRMD_RSC_RUN_TIME, (int *)&event.t_run);
         crm_element_value_int(msg, F_LRMD_RSC_RCCHANGE_TIME, (int *)&event.t_rcchange);
         crm_element_value_int(msg, F_LRMD_RSC_EXEC_TIME, (int *)&event.exec_time);
         crm_element_value_int(msg, F_LRMD_RSC_QUEUE_TIME, (int *)&event.queue_time);
 
         event.op_type = crm_element_value(msg, F_LRMD_RSC_ACTION);
         event.user_data = crm_element_value(msg, F_LRMD_RSC_USERDATA_STR);
         event.output = crm_element_value(msg, F_LRMD_RSC_OUTPUT);
         event.type = lrmd_event_exec_complete;
 
         event.params = xml2list(msg);
     }
 
     native->callback(&event);
 
     if (event.params) {
         g_hash_table_destroy(event.params);
     }
     free_xml(msg);
     return 1;
 }
 
 /* Not used with mainloop */
 bool
 lrmd_dispatch(lrmd_t * lrmd)
 {
     gboolean stay_connected = TRUE;
     lrmd_private_t *private = NULL;
 
     CRM_ASSERT(lrmd != NULL);
     private = lrmd->private;
     while (crm_ipc_ready(private->ipc)) {
         if (crm_ipc_read(private->ipc) > 0) {
             const char *msg = crm_ipc_buffer(private->ipc);
 
             lrmd_dispatch_internal(msg, strlen(msg), lrmd);
         }
         if (crm_ipc_connected(private->ipc) == FALSE) {
             crm_err("Connection closed");
             stay_connected = FALSE;
         }
     }
 
     return stay_connected;
 }
 
 static xmlNode *
 lrmd_create_op(int call_id,
                const char *token, const char *op, xmlNode * data, enum lrmd_call_options options)
 {
     xmlNode *op_msg = create_xml_node(NULL, "lrmd_command");
 
     CRM_CHECK(op_msg != NULL, return NULL);
     CRM_CHECK(token != NULL, return NULL);
 
     crm_xml_add(op_msg, F_XML_TAGNAME, "lrmd_command");
 
     crm_xml_add(op_msg, F_TYPE, T_LRMD);
     crm_xml_add(op_msg, F_LRMD_CALLBACK_TOKEN, token);
     crm_xml_add(op_msg, F_LRMD_OPERATION, op);
     crm_xml_add_int(op_msg, F_LRMD_CALLID, call_id);
     crm_trace("Sending call options: %.8lx, %d", (long)options, options);
     crm_xml_add_int(op_msg, F_LRMD_CALLOPTS, options);
 
     if (data != NULL) {
         add_message_xml(op_msg, F_LRMD_CALLDATA, data);
     }
 
     return op_msg;
 }
 
 static void
 lrmd_connection_destroy(gpointer userdata)
 {
     lrmd_t *lrmd = userdata;
     lrmd_private_t *native = lrmd->private;
 
     crm_info("connection destroyed");
 
     /* Prevent these from being cleaned up in lrmd_api_disconnect() */
     native->ipc = NULL;
     native->source = NULL;
 
     if (native->callback) {
         lrmd_event_data_t event = { 0, };
         event.type = lrmd_event_disconnect;
         native->callback(&event);
     }
 }
 
 static int
 lrmd_send_command(lrmd_t * lrmd, const char *op, xmlNode * data, xmlNode ** output_data, int timeout,   /* ms. defaults to 1000 if set to 0 */
                   enum lrmd_call_options options)
 {
     int rc = pcmk_ok;
     int reply_id = -1;
     lrmd_private_t *native = lrmd->private;
     xmlNode *op_msg = NULL;
     xmlNode *op_reply = NULL;
 
     if (!native->ipc) {
         return -ENOTCONN;
     }
 
     if (op == NULL) {
         crm_err("No operation specified");
         return -EINVAL;
     }
 
     native->call_id++;
     if (native->call_id < 1) {
         native->call_id = 1;
     }
 
     CRM_CHECK(native->token != NULL,;);
 
     op_msg = lrmd_create_op(native->call_id, native->token, op, data, options);
 
     if (op_msg == NULL) {
         return -EINVAL;
     }
 
     crm_xml_add_int(op_msg, F_LRMD_TIMEOUT, timeout);
 
     rc = crm_ipc_send(native->ipc, op_msg, &op_reply, timeout);
     free_xml(op_msg);
 
     if (rc < 0) {
         crm_perror(LOG_ERR, "Couldn't perform %s operation (timeout=%d): %d", op, timeout, rc);
         rc = -ECOMM;
         goto done;
     }
 
     rc = pcmk_ok;
     crm_element_value_int(op_reply, F_LRMD_CALLID, &reply_id);
     if (reply_id == native->call_id) {
         crm_trace("reply received");
         if (crm_element_value_int(op_reply, F_LRMD_RC, &rc) != 0) {
             rc = -ENOMSG;
             goto done;
         }
 
         if (output_data) {
             *output_data = op_reply;
             op_reply = NULL;    /* Prevent subsequent free */
         }
 
     } else if (reply_id <= 0) {
         crm_err("Recieved bad reply: No id set");
         crm_log_xml_err(op_reply, "Bad reply");
         rc = -ENOMSG;
     } else {
         crm_err("Recieved bad reply: %d (wanted %d)", reply_id, native->call_id);
         crm_log_xml_err(op_reply, "Old reply");
         rc = -ENOMSG;
     }
 
     crm_log_xml_trace(op_reply, "Reply");
 
   done:
     if (crm_ipc_connected(native->ipc) == FALSE) {
         crm_err("LRMD disconnected");
     }
 
     free_xml(op_reply);
     return rc;
 }
 
 static int
 lrmd_api_connect(lrmd_t * lrmd, const char *name, int *fd)
 {
     int rc = pcmk_ok;
     lrmd_private_t *native = lrmd->private;
 
     static struct ipc_client_callbacks lrmd_callbacks = {
         .dispatch = lrmd_dispatch_internal,
         .destroy = lrmd_connection_destroy
     };
 
     crm_info("Connecting to lrmd");
 
     if (fd) {
         /* No mainloop */
         native->ipc = crm_ipc_new("lrmd", 0);
         if (native->ipc && crm_ipc_connect(native->ipc)) {
             *fd = crm_ipc_get_fd(native->ipc);
         } else if (native->ipc) {
             rc = -ENOTCONN;
         }
     } else {
         native->source = mainloop_add_ipc_client("lrmd", G_PRIORITY_HIGH, 0, lrmd, &lrmd_callbacks);
         native->ipc = mainloop_get_ipc_client(native->source);
     }
 
     if (native->ipc == NULL) {
         crm_debug("Could not connect to the LRMD API");
         rc = -ENOTCONN;
     }
 
     if (!rc) {
         xmlNode *reply = NULL;
         xmlNode *hello = create_xml_node(NULL, "lrmd_command");
 
         crm_xml_add(hello, F_TYPE, T_LRMD);
         crm_xml_add(hello, F_LRMD_OPERATION, CRM_OP_REGISTER);
         crm_xml_add(hello, F_LRMD_CLIENTNAME, name);
 
         rc = crm_ipc_send(native->ipc, hello, &reply, -1);
 
         if (rc < 0) {
             crm_perror(LOG_DEBUG, "Couldn't complete registration with the lrmd API: %d", rc);
             rc = -ECOMM;
         } else if (reply == NULL) {
             crm_err("Did not receive registration reply");
             rc = -EPROTO;
         } else {
             const char *msg_type = crm_element_value(reply, F_LRMD_OPERATION);
             const char *tmp_ticket = crm_element_value(reply, F_LRMD_CLIENTID);
 
             if (safe_str_neq(msg_type, CRM_OP_REGISTER)) {
                 crm_err("Invalid registration message: %s", msg_type);
                 crm_log_xml_err(reply, "Bad reply");
                 rc = -EPROTO;
             } else if (tmp_ticket == NULL) {
                 crm_err("No registration token provided");
                 crm_log_xml_err(reply, "Bad reply");
                 rc = -EPROTO;
             } else {
                 crm_trace("Obtained registration token: %s", tmp_ticket);
                 native->token = strdup(tmp_ticket);
                 rc = pcmk_ok;
             }
         }
 
         free_xml(reply);
         free_xml(hello);
     }
 
     return rc;
 }
 
 static int
 lrmd_api_disconnect(lrmd_t * lrmd)
 {
     lrmd_private_t *native = lrmd->private;
 
     crm_info("Disconnecting from lrmd service");
 
     if (native->source) {
         mainloop_del_ipc_client(native->source);
         native->source = NULL;
         native->ipc = NULL;
     } else if (native->ipc) {
         crm_ipc_close(native->ipc);
         crm_ipc_destroy(native->ipc);
         native->source = NULL;
         native->ipc = NULL;
     }
 
     free(native->token);
     native->token = NULL;
     return 0;
 }
 
 static int
 lrmd_api_register_rsc(lrmd_t * lrmd,
                       const char *rsc_id,
                       const char *class,
                       const char *provider, const char *type, enum lrmd_call_options options)
 {
     int rc = pcmk_ok;
     xmlNode *data = NULL;
 
     if (!class || !type || !rsc_id) {
         return -EINVAL;
     }
     if (safe_str_eq(class, "ocf") && !provider) {
         return -EINVAL;
     }
 
     data = create_xml_node(NULL, F_LRMD_RSC);
 
     crm_xml_add(data, F_LRMD_ORIGIN, __FUNCTION__);
     crm_xml_add(data, F_LRMD_RSC_ID, rsc_id);
     crm_xml_add(data, F_LRMD_CLASS, class);
     crm_xml_add(data, F_LRMD_PROVIDER, provider);
     crm_xml_add(data, F_LRMD_TYPE, type);
     rc = lrmd_send_command(lrmd, LRMD_OP_RSC_REG, data, NULL, 0, options);
     free_xml(data);
 
     return rc;
 }
 
 static int
 lrmd_api_unregister_rsc(lrmd_t * lrmd, const char *rsc_id, enum lrmd_call_options options)
 {
     int rc = pcmk_ok;
     xmlNode *data = create_xml_node(NULL, F_LRMD_RSC);
 
     crm_xml_add(data, F_LRMD_ORIGIN, __FUNCTION__);
     crm_xml_add(data, F_LRMD_RSC_ID, rsc_id);
     rc = lrmd_send_command(lrmd, LRMD_OP_RSC_UNREG, data, NULL, 0, options);
     free_xml(data);
 
     return rc;
 }
 
 lrmd_rsc_info_t *
 lrmd_copy_rsc_info(lrmd_rsc_info_t * rsc_info)
 {
     lrmd_rsc_info_t *copy = NULL;
 
     copy = calloc(1, sizeof(lrmd_rsc_info_t));
 
     copy->id = strdup(rsc_info->id);
     copy->type = strdup(rsc_info->type);
     copy->class = strdup(rsc_info->class);
     if (rsc_info->provider) {
         copy->provider = strdup(rsc_info->provider);
     }
 
     return copy;
 }
 
 void
 lrmd_free_rsc_info(lrmd_rsc_info_t * rsc_info)
 {
     if (!rsc_info) {
         return;
     }
     free(rsc_info->id);
     free(rsc_info->type);
     free(rsc_info->class);
     free(rsc_info->provider);
     free(rsc_info);
 }
 
 static lrmd_rsc_info_t *
 lrmd_api_get_rsc_info(lrmd_t * lrmd, const char *rsc_id, enum lrmd_call_options options)
 {
     int rc = pcmk_ok;
     lrmd_rsc_info_t *rsc_info = NULL;
     xmlNode *data = create_xml_node(NULL, F_LRMD_RSC);
     xmlNode *output = NULL;
     const char *class = NULL;
     const char *provider = NULL;
     const char *type = NULL;
 
     crm_xml_add(data, F_LRMD_ORIGIN, __FUNCTION__);
     crm_xml_add(data, F_LRMD_RSC_ID, rsc_id);
     rc = lrmd_send_command(lrmd, LRMD_OP_RSC_INFO, data, &output, 0, options);
     free_xml(data);
 
     class = crm_element_value(output, F_LRMD_CLASS);
     provider = crm_element_value(output, F_LRMD_PROVIDER);
     type = crm_element_value(output, F_LRMD_TYPE);
 
     if (!output) {
         return NULL;
     } else if (!class || !type) {
         free_xml(output);
         return NULL;
     } else if (safe_str_eq(class, "ocf") && !provider) {
         free_xml(output);
         return NULL;
     }
 
     rsc_info = calloc(1, sizeof(lrmd_rsc_info_t));
     rsc_info->id = strdup(rsc_id);
     rsc_info->class = strdup(class);
     if (provider) {
         rsc_info->provider = strdup(provider);
     }
     rsc_info->type = strdup(type);
 
     free_xml(output);
     return rsc_info;
 }
 
 static void
 lrmd_api_set_callback(lrmd_t * lrmd, lrmd_event_callback callback)
 {
     lrmd_private_t *native = lrmd->private;
 
     native->callback = callback;
 }
 
 static int
 stonith_get_metadata(const char *provider, const char *type, char **output)
 {
     int rc = pcmk_ok;
 
     stonith_api->cmds->metadata(stonith_api, st_opt_sync_call, type, provider, output, 0);
     if (*output == NULL) {
         rc = -EIO;
     }
     return rc;
 }
 
 static int
 lsb_get_metadata(const char *type, char **output)
 {
 
 #define lsb_metadata_template  \
 "<?xml version=\"1.0\"?>\n"\
 "<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n"\
 "<resource-agent name=\"%s\" version=\"0.1\">\n"\
 "  <version>1.0</version>\n"\
 "  <longdesc lang=\"en\">\n"\
 "    %s"\
 "  </longdesc>\n"\
 "  <shortdesc lang=\"en\">%s</shortdesc>\n"\
 "  <parameters>\n"\
 "  </parameters>\n"\
 "  <actions>\n"\
 "    <action name=\"start\"   timeout=\"15\" />\n"\
 "    <action name=\"stop\"    timeout=\"15\" />\n"\
 "    <action name=\"status\"  timeout=\"15\" />\n"\
 "    <action name=\"restart\"  timeout=\"15\" />\n"\
 "    <action name=\"force-reload\"  timeout=\"15\" />\n"\
 "    <action name=\"monitor\" timeout=\"15\" interval=\"15\" />\n"\
 "    <action name=\"meta-data\"  timeout=\"5\" />\n"\
 "  </actions>\n"\
 "  <special tag=\"LSB\">\n"\
 "    <Provides>%s</Provides>\n"\
 "    <Required-Start>%s</Required-Start>\n"\
 "    <Required-Stop>%s</Required-Stop>\n"\
 "    <Should-Start>%s</Should-Start>\n"\
 "    <Should-Stop>%s</Should-Stop>\n"\
 "    <Default-Start>%s</Default-Start>\n"\
 "    <Default-Stop>%s</Default-Stop>\n"\
 "  </special>\n"\
 "</resource-agent>\n"
 
 #define LSB_INITSCRIPT_INFOBEGIN_TAG "### BEGIN INIT INFO"
 #define LSB_INITSCRIPT_INFOEND_TAG "### END INIT INFO"
 #define PROVIDES    "# Provides:"
 #define REQ_START   "# Required-Start:"
 #define REQ_STOP    "# Required-Stop:"
 #define SHLD_START  "# Should-Start:"
 #define SHLD_STOP   "# Should-Stop:"
 #define DFLT_START  "# Default-Start:"
 #define DFLT_STOP   "# Default-Stop:"
 #define SHORT_DSCR  "# Short-Description:"
 #define DESCRIPTION "# Description:"
 
 #define lsb_meta_helper_free_value(m)   \
     if ((m) != NULL) {                  \
         xmlFree(m);                     \
         (m) = NULL;                     \
     }
 
 #define lsb_meta_helper_get_value(buffer, ptr, keyword)                 \
     if (!ptr && !strncasecmp(buffer, keyword, strlen(keyword))) {       \
         (ptr) = (char *)xmlEncodeEntitiesReentrant(NULL, BAD_CAST buffer+strlen(keyword)); \
         continue;                                                       \
     }
 
     char ra_pathname[PATH_MAX] = { 0, };
     FILE *fp;
     GString *meta_data = NULL;
     char buffer[1024];
     char *provides = NULL;
     char *req_start = NULL;
     char *req_stop = NULL;
     char *shld_start = NULL;
     char *shld_stop = NULL;
     char *dflt_start = NULL;
     char *dflt_stop = NULL;
     char *s_dscrpt = NULL;
     char *xml_l_dscrpt = NULL;
     GString *l_dscrpt = NULL;
 
     snprintf(ra_pathname, sizeof(ra_pathname), "%s%s%s",
              type[0] == '/' ? "" : LSB_ROOT_DIR, type[0] == '/' ? "" : "/", type);
 
     if (!(fp = fopen(ra_pathname, "r"))) {
         return -EIO;
     }
 
     /* Enter into the lsb-compliant comment block */
     while (fgets(buffer, sizeof(buffer), fp)) {
         /* Now suppose each of the following eight arguments contain only one line */
         lsb_meta_helper_get_value(buffer, provides, PROVIDES)
             lsb_meta_helper_get_value(buffer, req_start, REQ_START)
             lsb_meta_helper_get_value(buffer, req_stop, REQ_STOP)
             lsb_meta_helper_get_value(buffer, shld_start, SHLD_START)
             lsb_meta_helper_get_value(buffer, shld_stop, SHLD_STOP)
             lsb_meta_helper_get_value(buffer, dflt_start, DFLT_START)
             lsb_meta_helper_get_value(buffer, dflt_stop, DFLT_STOP)
             lsb_meta_helper_get_value(buffer, s_dscrpt, SHORT_DSCR)
 
             /* Long description may cross multiple lines */
             if ((l_dscrpt == NULL) && (0 == strncasecmp(buffer, DESCRIPTION, strlen(DESCRIPTION)))) {
             l_dscrpt = g_string_new(buffer + strlen(DESCRIPTION));
             /* Between # and keyword, more than one space, or a tab character,
              * indicates the continuation line.     Extracted from LSB init script standard */
             while (fgets(buffer, sizeof(buffer), fp)) {
                 if (!strncmp(buffer, "#  ", 3) || !strncmp(buffer, "#\t", 2)) {
                     buffer[0] = ' ';
                     l_dscrpt = g_string_append(l_dscrpt, buffer);
                 } else {
                     fputs(buffer, fp);
                     break;      /* Long description ends */
                 }
             }
             continue;
         }
         if (l_dscrpt) {
             xml_l_dscrpt = (char *)xmlEncodeEntitiesReentrant(NULL, BAD_CAST(l_dscrpt->str));
         }
         if (!strncasecmp(buffer, LSB_INITSCRIPT_INFOEND_TAG, strlen(LSB_INITSCRIPT_INFOEND_TAG))) {
             /* Get to the out border of LSB comment block */
             break;
         }
         if (buffer[0] != '#') {
             break;              /* Out of comment block in the beginning */
         }
     }
     fclose(fp);
 
     meta_data = g_string_new("");
     g_string_sprintf(meta_data, lsb_metadata_template, type,
                      (xml_l_dscrpt == NULL) ? type : xml_l_dscrpt,
                      (s_dscrpt == NULL) ? type : s_dscrpt, (provides == NULL) ? "" : provides,
                      (req_start == NULL) ? "" : req_start, (req_stop == NULL) ? "" : req_stop,
                      (shld_start == NULL) ? "" : shld_start, (shld_stop == NULL) ? "" : shld_stop,
                      (dflt_start == NULL) ? "" : dflt_start, (dflt_stop == NULL) ? "" : dflt_stop);
 
     lsb_meta_helper_free_value(xml_l_dscrpt);
     lsb_meta_helper_free_value(s_dscrpt);
     lsb_meta_helper_free_value(provides);
     lsb_meta_helper_free_value(req_start);
     lsb_meta_helper_free_value(req_stop);
     lsb_meta_helper_free_value(shld_start);
     lsb_meta_helper_free_value(shld_stop);
     lsb_meta_helper_free_value(dflt_start);
     lsb_meta_helper_free_value(dflt_stop);
 
     if (l_dscrpt) {
         g_string_free(l_dscrpt, TRUE);
     }
 
     *output = strdup(meta_data->str);
     g_string_free(meta_data, TRUE);
 
     return pcmk_ok;
 }
 
 static int
 generic_get_metadata(const char *standard, const char *provider, const char *type, char **output)
 {
     svc_action_t *action = resources_action_create(type,
                                                    standard,
                                                    provider,
                                                    type,
                                                    "meta-data",
                                                    0,
                                                    5000,
                                                    NULL);
 
     if (!(services_action_sync(action))) {
         crm_err("Failed to retrieve meta-data for %s:%s:%s", standard, provider, type);
         services_action_free(action);
         return -EIO;
     }
 
     if (!action->stdout_data) {
         crm_err("Failed to retrieve meta-data for %s:%s:%s", standard, provider, type);
         services_action_free(action);
         return -EIO;
     }
 
     *output = strdup(action->stdout_data);
     services_action_free(action);
 
     return pcmk_ok;
 }
 
 static int
 lrmd_api_get_metadata(lrmd_t * lrmd,
                       const char *class,
                       const char *provider,
                       const char *type, char **output, enum lrmd_call_options options)
 {
     if (!class || !type) {
         return -EINVAL;
     }
 
     if (safe_str_eq(class, "stonith")) {
         return stonith_get_metadata(provider, type, output);
     } else if (safe_str_eq(class, "lsb")) {
         return lsb_get_metadata(type, output);
     }
     return generic_get_metadata(class, provider, type, output);
 }
 
 static int
 lrmd_api_exec(lrmd_t * lrmd, const char *rsc_id, const char *action, const char *userdata, int interval,        /* ms */
               int timeout,      /* ms */
               int start_delay,  /* ms */
               enum lrmd_call_options options, lrmd_key_value_t * params)
 {
     int rc = pcmk_ok;
     xmlNode *data = create_xml_node(NULL, F_LRMD_RSC);
     xmlNode *args = create_xml_node(data, XML_TAG_ATTRS);
 
     crm_xml_add(data, F_LRMD_ORIGIN, __FUNCTION__);
     crm_xml_add(data, F_LRMD_RSC_ID, rsc_id);
     crm_xml_add(data, F_LRMD_RSC_ACTION, action);
     crm_xml_add(data, F_LRMD_RSC_USERDATA_STR, userdata);
     crm_xml_add_int(data, F_LRMD_RSC_INTERVAL, interval);
     crm_xml_add_int(data, F_LRMD_TIMEOUT, timeout);
     crm_xml_add_int(data, F_LRMD_RSC_START_DELAY, start_delay);
 
     for (; params; params = params->next) {
         hash2field((gpointer) params->key, (gpointer) params->value, args);
     }
 
     rc = lrmd_send_command(lrmd, LRMD_OP_RSC_EXEC, data, NULL, timeout, options);
     free_xml(data);
 
     lrmd_key_value_freeall(params);
     return rc;
 }
 
 static int
 lrmd_api_cancel(lrmd_t * lrmd, const char *rsc_id, const char *action, int interval)
 {
     int rc = pcmk_ok;
     xmlNode *data = create_xml_node(NULL, F_LRMD_RSC);
 
     crm_xml_add(data, F_LRMD_ORIGIN, __FUNCTION__);
     crm_xml_add(data, F_LRMD_RSC_ACTION, action);
     crm_xml_add(data, F_LRMD_RSC_ID, rsc_id);
     crm_xml_add_int(data, F_LRMD_RSC_INTERVAL, interval);
     rc = lrmd_send_command(lrmd, LRMD_OP_RSC_CANCEL, data, NULL, 0, 0);
     free_xml(data);
     return rc;
 }
 
 static int
 list_stonith_agents(lrmd_list_t ** resources)
 {
     int rc = 0;
     stonith_key_value_t *stonith_resources = NULL;
     stonith_key_value_t *dIter = NULL;
 
     stonith_api->cmds->list_agents(stonith_api, st_opt_sync_call, NULL, &stonith_resources, 0);
 
     for (dIter = stonith_resources; dIter; dIter = dIter->next) {
         rc++;
         if(resources) {
             *resources = lrmd_list_add(*resources, dIter->value);
         }
     }
 
     stonith_key_value_freeall(stonith_resources, 1, 0);
     return rc;
 }
 
 static int
 lrmd_api_list_agents(lrmd_t * lrmd, lrmd_list_t ** resources, const char *class,
                      const char *provider)
 {
     int rc = 0;
 
     if (safe_str_eq(class, "stonith")) {
         rc += list_stonith_agents(resources);
 
     } else {
         GListPtr gIter = NULL;
         GList *agents = resources_list_agents(class, provider);
 
         for (gIter = agents; gIter != NULL; gIter = gIter->next) {
             *resources = lrmd_list_add(*resources, (const char *)gIter->data);
             rc++;
         }
         g_list_free_full(agents, free);
 
         if (!class) {
             rc += list_stonith_agents(resources);
         }
     }
 
     if(rc == 0) {
         crm_notice("No agents found for class %s", class);
         rc = -EPROTONOSUPPORT;
     }
     return rc;
 }
 
 static int
 does_provider_have_agent(const char *agent, const char *provider, const char *class)
 {
     int found = 0;
     GList *agents = NULL;
     GListPtr gIter2 = NULL;
 
     agents = resources_list_agents(class, provider);
     for (gIter2 = agents; gIter2 != NULL; gIter2 = gIter2->next) {
         if (safe_str_eq(agent, gIter2->data)) {
             found = 1;
         }
     }
     g_list_free_full(agents, free);
 
     return found;
 }
 
 static int
 lrmd_api_list_ocf_providers(lrmd_t * lrmd, const char *agent, lrmd_list_t ** providers)
 {
     int rc = pcmk_ok;
     char *provider = NULL;
     GList *ocf_providers = NULL;
     GListPtr gIter = NULL;
 
     ocf_providers = resources_list_providers("ocf");
 
     for (gIter = ocf_providers; gIter != NULL; gIter = gIter->next) {
         provider = gIter->data;
         if (!agent || does_provider_have_agent(agent, provider, "ocf")) {
             *providers = lrmd_list_add(*providers, (const char *)gIter->data);
             rc++;
         }
     }
 
     g_list_free_full(ocf_providers, free);
     return rc;
 }
 
 static int
 lrmd_api_list_standards(lrmd_t * lrmd, lrmd_list_t ** supported)
 {
     int rc = 0;
     char *standard = NULL;
     GList *standards = NULL;
     GListPtr gIter = NULL;
 
     standards = resources_list_standards();
 
     for (gIter = standards; gIter != NULL; gIter = gIter->next) {
         standard = gIter->data;
         *supported = lrmd_list_add(*supported, (const char *)gIter->data);
         rc++;
     }
 
     if(list_stonith_agents(NULL) > 0) {
         *supported = lrmd_list_add(*supported, "stonith");
         rc++;
     }
 
     g_list_free_full(standards, free);
     return rc;
 }
 
 lrmd_t *
 lrmd_api_new(void)
 {
     lrmd_t *new_lrmd = NULL;
     lrmd_private_t *pvt = NULL;
 
     new_lrmd = calloc(1, sizeof(lrmd_t));
     pvt = calloc(1, sizeof(lrmd_private_t));
     new_lrmd->cmds = calloc(1, sizeof(lrmd_api_operations_t));
 
     new_lrmd->private = pvt;
 
     new_lrmd->cmds->connect = lrmd_api_connect;
     new_lrmd->cmds->disconnect = lrmd_api_disconnect;
     new_lrmd->cmds->register_rsc = lrmd_api_register_rsc;
     new_lrmd->cmds->unregister_rsc = lrmd_api_unregister_rsc;
     new_lrmd->cmds->get_rsc_info = lrmd_api_get_rsc_info;
     new_lrmd->cmds->set_callback = lrmd_api_set_callback;
     new_lrmd->cmds->get_metadata = lrmd_api_get_metadata;
     new_lrmd->cmds->exec = lrmd_api_exec;
     new_lrmd->cmds->cancel = lrmd_api_cancel;
     new_lrmd->cmds->list_agents = lrmd_api_list_agents;
     new_lrmd->cmds->list_ocf_providers = lrmd_api_list_ocf_providers;
     new_lrmd->cmds->list_standards = lrmd_api_list_standards;
 
     if (!stonith_api) {
         stonith_api = stonith_api_new();
     }
 
     return new_lrmd;
 }
 
 void
 lrmd_api_delete(lrmd_t * lrmd)
 {
+    if (!lrmd) {
+        return;
+    }
     lrmd->cmds->disconnect(lrmd);       /* no-op if already disconnected */
     free(lrmd->cmds);
     free(lrmd->private);
     free(lrmd);
 }
diff --git a/lrmd/regression.py.in b/lrmd/regression.py.in
index 553e7b09ec..de42d9356f 100755
--- a/lrmd/regression.py.in
+++ b/lrmd/regression.py.in
@@ -1,780 +1,819 @@
 #!/usr/bin/python
 
 #
 # 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
 # of the License, 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; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
 
 import os
 import sys
 import subprocess
 import shlex
 import time
 def output_from_command(command, no_wait=0):
 	test = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE)
 
 	if no_wait == 0:
 		test.wait()
 	else:
 		return 0
 
 	return test.communicate()[0].split("\n")
 
 class Test:
 	def __init__(self, name, description, verbose = 0):
 		self.name = name
 		self.description = description
 		self.cmds = []
 		self.daemon_location = "@CRM_DAEMON_DIR@/lrmd"
 		self.test_tool_location = "@CRM_DAEMON_DIR@/lrmd_test"
 		self.verbose = verbose
 
 		self.result_txt = ""
 		self.cmd_tool_output = ""
 		self.result_exitcode = 0;
 
 		self.lrmd_process = None
 		self.stonith_process = None
 
 		self.executed = 0
 
 	def __new_cmd(self, cmd, args, exitcode, stdout_match = "", no_wait = 0, stdout_negative_match = "", kill=None):
 		if self.verbose and cmd == self.test_tool_location:
 			args = args + " -V "
 
 		self.cmds.append(
 			{
 				"cmd" : cmd,
 				"kill" : kill,
 				"args" : args,
 				"expected_exitcode" : exitcode,
 				"stdout_match" : stdout_match,
 				"stdout_negative_match" : stdout_negative_match,
 				"no_wait" : no_wait,
 				"cmd_output" : "",
 			}
 		)
 
 	def start_environment(self):
 		### make sure we are in full control here ###
 		cmd = shlex.split("killall -q -9 stonithd lrmd lt-lrmd lrmd_test lt-lrmd_test")
 		test = subprocess.Popen(cmd, stdout=subprocess.PIPE)
 		test.wait()
 
 		self.stonith_process = subprocess.Popen(shlex.split("@CRM_DAEMON_DIR@/stonithd -s"))
 		if self.verbose:
 			self.lrmd_process = subprocess.Popen(shlex.split("%s -VVV -l /tmp/lrmd-regression.log" % self.daemon_location))
 		else:
 			self.lrmd_process = subprocess.Popen(shlex.split("%s -l /tmp/lrmd-regression.log" % self.daemon_location))
 
 		time.sleep(1)
 
 	def clean_environment(self):
 		if self.lrmd_process:
-			self.lrmd_process.kill()
+			self.lrmd_process.terminate()
+			self.lrmd_process.wait()
 		if self.stonith_process:
-			self.stonith_process.kill()
+			self.stonith_process.terminate()
+			self.stonith_process.wait()
 
 		self.lrmd_process = None
 		self.stonith_process = None
 
 	def add_sys_cmd(self, cmd, args):
 		self.__new_cmd(cmd, args, 0, "")
 
 	def add_sys_cmd_no_wait(self, cmd, args):
 		self.__new_cmd(cmd, args, 0, "", 1)
 
 	def add_cmd_check_stdout(self, args, match, no_match = ""):
 		self.__new_cmd(self.test_tool_location, args, 0, match, 0, no_match)
 
 	def add_cmd(self, args):
 		self.__new_cmd(self.test_tool_location, args, 0, "")
 
 	def add_cmd_and_kill(self, killProc, args):
 		self.__new_cmd(self.test_tool_location, args, 0, "", kill=killProc)
 
 	def add_expected_fail_cmd(self, args):
 		self.__new_cmd(self.test_tool_location, args, 255, "")
 
 	def get_exitcode(self):
 		return self.result_exitcode
 
 	def print_result(self, filler):
 		print "%s%s" % (filler, self.result_txt)
 
 	def run_cmd(self, args):
 		cmd = shlex.split(args['args'])
 		cmd.insert(0, args['cmd'])
 		if self.verbose:
 			print "\n\nRunning: "+" ".join(cmd)
 		test = subprocess.Popen(cmd, stdout=subprocess.PIPE)
 
 		if args['kill']:
 			if self.verbose:
 				print "Also running: "+args['kill']
 			subprocess.Popen(shlex.split(args['kill']))
 
 		if args['no_wait'] == 0:
 			test.wait()
 		else:
 			return 0
 
 		output = test.communicate()[0]
 
 		if args['stdout_match'] != "" and output.count(args['stdout_match']) == 0:
 			test.returncode = -2
 			print "STDOUT string '%s' was not found in cmd output" % (args['stdout_match'])
 
 		if args['stdout_negative_match'] != "" and output.count(args['stdout_negative_match']) != 0:
 			test.returncode = -2
 			print "STDOUT string '%s' was found in cmd output" % (args['stdout_negative_match'])
 
 		args['cmd_output'] = output
 
 		return test.returncode;
 
 	def run(self):
 		res = 0
 		i = 1
 		self.start_environment()
 
 		if self.verbose:
 			print "\n--- START TEST - %s" % self.name
 
 		self.result_txt = "SUCCESS - '%s'" % (self.name)
 		self.result_exitcode = 0
 		for cmd in self.cmds:
 			res = self.run_cmd(cmd)
 			if res != cmd['expected_exitcode']:
 				print cmd['cmd_output']
 				print "Step %d FAILED - command returned %d, expected %d" % (i, res, cmd['expected_exitcode'])
 				self.result_txt = "FAILURE - '%s' failed at step %d. Command: lrmd_test %s" % (self.name, i, cmd['args'])
 				self.result_exitcode = -1
 				break
 			else:
 				if self.verbose:
 					print cmd['cmd_output'].strip()
 					print "Step %d SUCCESS" % (i)
 			i = i + 1
 		self.clean_environment()
 
 		print self.result_txt
 		if self.verbose:
 			print "--- END TEST - %s\n" % self.name
 
 		self.executed = 1
 		return res
 
 class Tests:
 	def __init__(self, verbose = 0):
 		self.tests = []
 		self.verbose = verbose
 
 		self.rsc_classes = output_from_command("crm_resource --list-standards")		
 		self.rsc_classes = self.rsc_classes[:-1] # Strip trailing empty line
 		print "Testing "+repr(self.rsc_classes)
 
 		self.common_cmds = {
 			"ocf_reg_line"      : "-c register_rsc -r ocf_test_rsc -t 1000 -C ocf -P pacemaker -T Dummy",
 			"ocf_reg_event"     : "-l \"NEW_EVENT event_type:register rsc_id:ocf_test_rsc action:none rc:ok op_status:complete\"",
 			"ocf_unreg_line"    : "-c unregister_rsc -r \"ocf_test_rsc\" -t 1000",
 			"ocf_unreg_event"   : "-l \"NEW_EVENT event_type:unregister rsc_id:ocf_test_rsc action:none rc:ok op_status:complete\"",
 			"ocf_start_line"    : "-c exec -r \"ocf_test_rsc\" -a \"start\" -t 1000 ",
 			"ocf_start_event"   : "-l \"NEW_EVENT event_type:exec_complete rsc_id:ocf_test_rsc action:start rc:ok op_status:complete\" ",
 			"ocf_stop_line"     : "-c exec -r \"ocf_test_rsc\" -a \"stop\" -t 1000 ",
 			"ocf_stop_event"    : "-l \"NEW_EVENT event_type:exec_complete rsc_id:ocf_test_rsc action:stop rc:ok op_status:complete\" ",
 			"ocf_monitor_line"  : "-c exec -r \"ocf_test_rsc\" -a \"monitor\" -i \"1000\" -t 1000",
 			"ocf_monitor_event" : "-l \"NEW_EVENT event_type:exec_complete rsc_id:ocf_test_rsc action:monitor rc:ok op_status:complete\" -t 2000",
 			"ocf_cancel_line"   : "-c cancel -r \"ocf_test_rsc\" -a \"monitor\" -i \"1000\" -t \"1000\" ",
 			"ocf_cancel_event"  : "-l \"NEW_EVENT event_type:exec_complete rsc_id:ocf_test_rsc action:monitor rc:ok op_status:Cancelled\" ",
 
 			"systemd_reg_line"      : "-c register_rsc -r systemd_test_rsc -t 1000 -C systemd -T lrmd_dummy_daemon",
 			"systemd_reg_event"     : "-l \"NEW_EVENT event_type:register rsc_id:systemd_test_rsc action:none rc:ok op_status:complete\"",
 			"systemd_unreg_line"    : "-c unregister_rsc -r \"systemd_test_rsc\" -t 1000",
 			"systemd_unreg_event"   : "-l \"NEW_EVENT event_type:unregister rsc_id:systemd_test_rsc action:none rc:ok op_status:complete\"",
 			"systemd_start_line"    : "-c exec -r \"systemd_test_rsc\" -a \"start\" -t 1000 ",
 			"systemd_start_event"   : "-l \"NEW_EVENT event_type:exec_complete rsc_id:systemd_test_rsc action:start rc:ok op_status:complete\" ",
 			"systemd_stop_line"     : "-c exec -r \"systemd_test_rsc\" -a \"stop\" -t 1000 ",
 			"systemd_stop_event"    : "-l \"NEW_EVENT event_type:exec_complete rsc_id:systemd_test_rsc action:stop rc:ok op_status:complete\" ",
 			"systemd_monitor_line"  : "-c exec -r \"systemd_test_rsc\" -a \"monitor\" -i \"1000\" -t 1000",
 			"systemd_monitor_event" : "-l \"NEW_EVENT event_type:exec_complete rsc_id:systemd_test_rsc action:monitor rc:ok op_status:complete\" -t 2000",
 			"systemd_cancel_line"   : "-c cancel -r \"systemd_test_rsc\" -a \"monitor\" -i \"1000\" -t \"1000\" ",
 			"systemd_cancel_event"  : "-l \"NEW_EVENT event_type:exec_complete rsc_id:systemd_test_rsc action:monitor rc:ok op_status:Cancelled\" ",
 
 			"upstart_reg_line"      : "-c register_rsc -r upstart_test_rsc -t 1000 -C upstart -T lrmd_dummy_daemon",
 			"upstart_reg_event"     : "-l \"NEW_EVENT event_type:register rsc_id:upstart_test_rsc action:none rc:ok op_status:complete\"",
 			"upstart_unreg_line"    : "-c unregister_rsc -r \"upstart_test_rsc\" -t 1000",
 			"upstart_unreg_event"   : "-l \"NEW_EVENT event_type:unregister rsc_id:upstart_test_rsc action:none rc:ok op_status:complete\"",
 			"upstart_start_line"    : "-c exec -r \"upstart_test_rsc\" -a \"start\" -t 1000 ",
 			"upstart_start_event"   : "-l \"NEW_EVENT event_type:exec_complete rsc_id:upstart_test_rsc action:start rc:ok op_status:complete\" ",
 			"upstart_stop_line"     : "-c exec -r \"upstart_test_rsc\" -a \"stop\" -t 1000 ",
 			"upstart_stop_event"    : "-l \"NEW_EVENT event_type:exec_complete rsc_id:upstart_test_rsc action:stop rc:ok op_status:complete\" ",
 			"upstart_monitor_line"  : "-c exec -r \"upstart_test_rsc\" -a \"monitor\" -i \"1000\" -t 1000",
 			"upstart_monitor_event" : "-l \"NEW_EVENT event_type:exec_complete rsc_id:upstart_test_rsc action:monitor rc:ok op_status:complete\" -t 2000",
 			"upstart_cancel_line"   : "-c cancel -r \"upstart_test_rsc\" -a \"monitor\" -i \"1000\" -t \"1000\" ",
 			"upstart_cancel_event"  : "-l \"NEW_EVENT event_type:exec_complete rsc_id:upstart_test_rsc action:monitor rc:ok op_status:Cancelled\" ",
 
 			"service_reg_line"      : "-c register_rsc -r service_test_rsc -t 1000 -C service -T lrmd_dummy_daemon",
 			"service_reg_event"     : "-l \"NEW_EVENT event_type:register rsc_id:service_test_rsc action:none rc:ok op_status:complete\"",
 			"service_unreg_line"    : "-c unregister_rsc -r \"service_test_rsc\" -t 1000",
 			"service_unreg_event"   : "-l \"NEW_EVENT event_type:unregister rsc_id:service_test_rsc action:none rc:ok op_status:complete\"",
 			"service_start_line"    : "-c exec -r \"service_test_rsc\" -a \"start\" -t 1000 ",
 			"service_start_event"   : "-l \"NEW_EVENT event_type:exec_complete rsc_id:service_test_rsc action:start rc:ok op_status:complete\" ",
 			"service_stop_line"     : "-c exec -r \"service_test_rsc\" -a \"stop\" -t 1000 ",
 			"service_stop_event"    : "-l \"NEW_EVENT event_type:exec_complete rsc_id:service_test_rsc action:stop rc:ok op_status:complete\" ",
 			"service_monitor_line"  : "-c exec -r \"service_test_rsc\" -a \"monitor\" -i \"1000\" -t 1000",
 			"service_monitor_event" : "-l \"NEW_EVENT event_type:exec_complete rsc_id:service_test_rsc action:monitor rc:ok op_status:complete\" -t 2000",
 			"service_cancel_line"   : "-c cancel -r \"service_test_rsc\" -a \"monitor\" -i \"1000\" -t \"1000\" ",
 			"service_cancel_event"  : "-l \"NEW_EVENT event_type:exec_complete rsc_id:service_test_rsc action:monitor rc:ok op_status:Cancelled\" ",
 
 			"lsb_reg_line"      : "-c register_rsc -r lsb_test_rsc -t 1000 -C lsb -T LSBDummy",
 			"lsb_reg_event"     : "-l \"NEW_EVENT event_type:register rsc_id:lsb_test_rsc action:none rc:ok op_status:complete\" ",
 			"lsb_unreg_line"    : "-c unregister_rsc -r \"lsb_test_rsc\" -t 1000",
 			"lsb_unreg_event"   : "-l \"NEW_EVENT event_type:unregister rsc_id:lsb_test_rsc action:none rc:ok op_status:complete\"",
 			"lsb_start_line"    : "-c exec -r \"lsb_test_rsc\" -a \"start\" -t 1000 ",
 			"lsb_start_event"   : "-l \"NEW_EVENT event_type:exec_complete rsc_id:lsb_test_rsc action:start rc:ok op_status:complete\" ",
 			"lsb_stop_line"     : "-c exec -r \"lsb_test_rsc\" -a \"stop\" -t 1000 ",
 			"lsb_stop_event"    : "-l \"NEW_EVENT event_type:exec_complete rsc_id:lsb_test_rsc action:stop rc:ok op_status:complete\" ",
 			"lsb_monitor_line"  : "-c exec -r \"lsb_test_rsc\" -a status -i \"1000\" -t 1000",
 			"lsb_monitor_event" : "-l \"NEW_EVENT event_type:exec_complete rsc_id:lsb_test_rsc action:status rc:ok op_status:complete\" -t 2000",
 			"lsb_cancel_line"   : "-c cancel -r \"lsb_test_rsc\" -a \"status\" -i \"1000\" -t \"1000\" ",
 			"lsb_cancel_event"  : "-l \"NEW_EVENT event_type:exec_complete rsc_id:lsb_test_rsc action:status rc:ok op_status:Cancelled\" ",
 
 			"stonith_reg_line"      : "-c register_rsc -r stonith_test_rsc -t 1000 -C stonith -P pacemaker -T fence_dummy_monitor",
 			"stonith_reg_event"     : "-l \"NEW_EVENT event_type:register rsc_id:stonith_test_rsc action:none rc:ok op_status:complete\" ",
 			"stonith_unreg_line"    : "-c unregister_rsc -r \"stonith_test_rsc\" -t 1000",
 			"stonith_unreg_event"   : "-l \"NEW_EVENT event_type:unregister rsc_id:stonith_test_rsc action:none rc:ok op_status:complete\"",
 			"stonith_start_line"    : "-c exec -r \"stonith_test_rsc\" -a \"start\" -t 1000 ",
 			"stonith_start_event"   : "-l \"NEW_EVENT event_type:exec_complete rsc_id:stonith_test_rsc action:start rc:ok op_status:complete\" ",
 			"stonith_stop_line"     : "-c exec -r \"stonith_test_rsc\" -a \"stop\" -t 1000 ",
 			"stonith_stop_event"    : "-l \"NEW_EVENT event_type:exec_complete rsc_id:stonith_test_rsc action:stop rc:ok op_status:complete\" ",
 			"stonith_monitor_line"  : "-c exec -r \"stonith_test_rsc\" -a \"monitor\" -i \"1000\" -t 1000",
 			"stonith_monitor_event" : "-l \"NEW_EVENT event_type:exec_complete rsc_id:stonith_test_rsc action:monitor rc:ok op_status:complete\" -t 3000",
 			"stonith_cancel_line"   : "-c cancel -r \"stonith_test_rsc\" -a \"monitor\" -i \"1000\" -t \"1000\" ",
 			"stonith_cancel_event"  : "-l \"NEW_EVENT event_type:exec_complete rsc_id:stonith_test_rsc action:monitor rc:ok op_status:Cancelled\" ",
 		}
 
 	def new_test(self, name, description):
 		test = Test(name, description, self.verbose)
 		self.tests.append(test)
 		return test
 
 	def setup_test_environment(self):
 		self.cleanup_test_environment()
 
 		### Make fake systemd daemon and unit file ###
 		dummy_daemon = "#!/bin/bash\nwhile true\ndo\nsleep 5\ndone"
 		dummy_service_file = ("[Unit]\n"
 			"Description=Dummy Resource\n"
 			"[Service]\n"
 			"Type=simple\n"
 			"ExecStart=/usr/sbin/lrmd_dummy_daemon\n")
 		dummy_upstart_job = ("""
 description     "Dummy service for regression tests"
 exec dd if=/dev/random of=/dev/null
 """)
 		dummy_fence_agent = ("""#!/usr/bin/python
 import sys
 def main():
     for line in sys.stdin.readlines():
         if line.count("monitor") > 0:
             sys.exit(0)
         if line.count("metadata") > 0:
             print '<resource-agent name="fence_dummy_monitor" shortdesc="Dummy Fence agent for testing">'
             print '  <longdesc>dummy description.</longdesc>'
             print '  <vendor-url>http://www.example.com</vendor-url>'
             print '  <parameters>'
             print '    <parameter name="action" unique="0" required="1">'
             print '      <getopt mixed="-o, --action=[action]"/>'
             print '      <content type="string" default="reboot"/>'
             print '      <shortdesc lang="en">Fencing Action</shortdesc>'
             print '    </parameter>'
             print '    <parameter name="port" unique="0" required="0">'
             print '      <getopt mixed="-n, --plug=[id]"/>'
             print '      <content type="string"/>'
             print '      <shortdesc lang="en">Physical plug number or name of virtual machine</shortdesc>'
             print '    </parameter>'
             print '  </parameters>'
             print '  <actions>'
             print '    <action name="on"/>'
             print '    <action name="off"/>'
             print '    <action name="monitor"/>'
             print '    <action name="metadata"/>'
             print '  </actions>'
             print '</resource-agent>'
             sys.exit(0)
     sys.exit(-1)
 if __name__ == "__main__":
     main()
 """)
 
 		os.system("cat <<-END >>/etc/init/lrmd_dummy_daemon.conf\n%s\nEND" % (dummy_upstart_job))
 		os.system("cat <<-END >>/usr/sbin/lrmd_dummy_daemon\n%s\nEND" % (dummy_daemon))
 		os.system("cat <<-END >>/lib/systemd/system/lrmd_dummy_daemon.service\n%s\nEND" % (dummy_service_file))
 		os.system("chmod u+x /usr/sbin/lrmd_dummy_daemon")
 
 		os.system("cat <<-END >>/usr/sbin/fence_dummy_monitor\n%s\nEND" % (dummy_fence_agent))
 		os.system("chmod 711 /usr/sbin/fence_dummy_monitor")
 
 		os.system("cp /usr/share/pacemaker/tests/cts/LSBDummy /etc/init.d/LSBDummy")
 		os.system("mkdir -p @CRM_CORE_DIR@/root")
 
 		os.system("systemctl daemon-reload")
 
 	def cleanup_test_environment(self):
 		os.system("rm -f /lib/systemd/system/lrmd_dummy_daemon.service")
 		os.system("rm -f /usr/sbin/lrmd_dummy_daemon")
 		os.system("rm -f /usr/sbin/fence_dummy_monitor")
 		os.system("rm -f /etc/init.d/LSBDummy")
 		os.system("systemctl daemon-reload")
 
 	### These are tests that should apply to all resource classes ###
 	def build_generic_tests(self):
 		common_cmds = self.common_cmds
 
 		### register/unregister tests ###
 		for rsc in self.rsc_classes:
 			test = self.new_test("generic_registration_%s" % (rsc), "Simple resource registration test for %s standard" % (rsc))
 			test.add_cmd(common_cmds["%s_reg_line" % (rsc)] + " " + common_cmds["%s_reg_event" % (rsc)])
 			test.add_cmd(common_cmds["%s_unreg_line" % (rsc)] + " " + common_cmds["%s_unreg_event" % (rsc)])
 
 		### start/stop tests  ###
 		for rsc in self.rsc_classes:
 			test = self.new_test("generic_start_stop_%s" % (rsc), "Simple start and stop test for %s standard" % (rsc))
 			test.add_cmd(common_cmds["%s_reg_line" % (rsc)]   + " " + common_cmds["%s_reg_event" % (rsc)])
 			test.add_cmd(common_cmds["%s_start_line" % (rsc)] + " " + common_cmds["%s_start_event" % (rsc)])
 			test.add_cmd(common_cmds["%s_stop_line" % (rsc)]  + " " + common_cmds["%s_stop_event" % (rsc)])
 			test.add_cmd(common_cmds["%s_unreg_line" % (rsc)] + " " + common_cmds["%s_unreg_event" % (rsc)])
 
 		### monitor cancel test ###
 		for rsc in self.rsc_classes:
 			test = self.new_test("generic_monitor_cancel_%s" % (rsc), "Simple monitor cancel test for %s standard" % (rsc))
 			test.add_cmd(common_cmds["%s_reg_line" % (rsc)]   + " " + common_cmds["%s_reg_event" % (rsc)])
 			test.add_cmd(common_cmds["%s_start_line" % (rsc)] + " " + common_cmds["%s_start_event" % (rsc)])
 			test.add_cmd(common_cmds["%s_monitor_line" % (rsc)] + " " + common_cmds["%s_monitor_event" % (rsc)])
 			test.add_cmd(common_cmds["%s_monitor_event" % (rsc)]) ### If this fails, that means the monitor may not be getting rescheduled ####
 			test.add_cmd(common_cmds["%s_monitor_event" % (rsc)]) ### If this fails, that means the monitor may not be getting rescheduled ####
 			test.add_cmd(common_cmds["%s_cancel_line" % (rsc)] + " " + common_cmds["%s_cancel_event" % (rsc)])
 			test.add_expected_fail_cmd(common_cmds["%s_monitor_event" % (rsc)]) ### If this happens the monitor did not actually cancel correctly. ###
 			test.add_expected_fail_cmd(common_cmds["%s_monitor_event" % (rsc)]) ### If this happens the monitor did not actually cancel correctly. ###
 			test.add_cmd(common_cmds["%s_stop_line" % (rsc)]  + " " + common_cmds["%s_stop_event" % (rsc)])
 			test.add_cmd(common_cmds["%s_unreg_line" % (rsc)] + " " + common_cmds["%s_unreg_event" % (rsc)])
 
 	### These are complex tests that involve managing multiple resouces of different types ###
 	def build_multi_rsc_tests(self):
 		common_cmds = self.common_cmds
 		# do not use service and systemd at the same time, it is the same resource.
 
 		### register start monitor stop unregister resources of each type at the same time. ###
 		test = self.new_test("multi_rsc_start_stop_all", "Start, monitor, and stop resources of multiple types and classes")
 		for rsc in self.rsc_classes:
 			test.add_cmd(common_cmds["%s_reg_line" % (rsc)]   + " " + common_cmds["%s_reg_event" % (rsc)])
 		for rsc in self.rsc_classes:
 			test.add_cmd(common_cmds["%s_start_line" % (rsc)] + " " + common_cmds["%s_start_event" % (rsc)])
 		for rsc in self.rsc_classes:
 			test.add_cmd(common_cmds["%s_monitor_line" % (rsc)] + " " + common_cmds["%s_monitor_event" % (rsc)])
 		for rsc in self.rsc_classes:
 			test.add_cmd(common_cmds["%s_monitor_event" % (rsc)]) ### If this fails, that means the monitor is not being rescheduled ####
 		for rsc in self.rsc_classes:
 			test.add_cmd(common_cmds["%s_cancel_line" % (rsc)] + " " + common_cmds["%s_cancel_event" % (rsc)])
 		for rsc in self.rsc_classes:
 			test.add_cmd(common_cmds["%s_stop_line" % (rsc)]  + " " + common_cmds["%s_stop_event" % (rsc)])
 		for rsc in self.rsc_classes:
 			test.add_cmd(common_cmds["%s_unreg_line" % (rsc)] + " " + common_cmds["%s_unreg_event" % (rsc)])
 
 	### These are tests related to how the lrmd handles failures.  ###
 	def build_negative_tests(self):
 
 		### start timeout test  ###
 		test = self.new_test("start_timeout", "Force start timeout to occur, verify start failure.")
 		test.add_cmd("-c register_rsc -r \"test_rsc\" -C \"ocf\" -P \"pacemaker\" -T \"Dummy\" -t 1000 "
 			"-l \"NEW_EVENT event_type:register rsc_id:test_rsc action:none rc:ok op_status:complete\" ")
 		test.add_cmd("-c exec -r \"test_rsc\" -a \"start\" -k \"op_sleep\" -v \"3\" -t 1000 -w")
 		test.add_cmd("-l "
 			"\"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:start rc:unknown error op_status:Timed Out\" -t 3000")
 		test.add_cmd("-c exec -r test_rsc -a stop -t 1000"
 			"-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:stop rc:ok op_status:complete\" ")
 		test.add_cmd("-c unregister_rsc -r test_rsc -t 1000 "
 			"-l \"NEW_EVENT event_type:unregister rsc_id:test_rsc action:none rc:ok op_status:complete\" ")
 
 		### monitor fail for ocf resources ###
 		test = self.new_test("monitor_fail_ocf", "Force ocf monitor to fail, verify failure is reported.")
 		test.add_cmd("-c register_rsc -r \"test_rsc\" -C \"ocf\" -P \"pacemaker\" -T \"Dummy\" -t 1000 "
 			"-l \"NEW_EVENT event_type:register rsc_id:test_rsc action:none rc:ok op_status:complete\" ")
 		test.add_cmd("-c exec -r \"test_rsc\" -a \"start\" -t 1000 "
 			"-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:start rc:ok op_status:complete\" ")
 		test.add_cmd("-c exec -r \"test_rsc\" -a \"start\" -t 1000 "
 			"-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:start rc:ok op_status:complete\" ")
 		test.add_cmd("-c exec -r \"test_rsc\" -a \"monitor\" -i \"100\" -t 1000 "
 			"-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:monitor rc:ok op_status:complete\" ")
 		test.add_cmd("-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:monitor rc:ok op_status:complete\" -t 2000")
 		test.add_cmd("-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:monitor rc:ok op_status:complete\" -t 2000")
 		test.add_cmd_and_kill("rm -f /var/run/Dummy-test_rsc.state", "-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:monitor rc:not running op_status:complete\" -t 2000")
 		test.add_cmd("-c cancel -r \"test_rsc\" -a \"monitor\" -i \"100\" -t \"1000\" "
 			"-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:monitor rc:not running op_status:Cancelled\" ")
 		test.add_expected_fail_cmd("-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:monitor rc:not running op_status:complete\" -t 1000")
 		test.add_expected_fail_cmd("-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:monitor rc:ok op_status:complete\" -t 1000")
 		test.add_cmd("-c unregister_rsc -r \"test_rsc\" -t 1000 "
 			"-l \"NEW_EVENT event_type:unregister rsc_id:test_rsc action:none rc:ok op_status:complete\" ")
 
 		### monitor fail for systemd resource ###
 		if "systemd" in self.rsc_classes:
 			test = self.new_test("monitor_fail_systemd", "Force systemd monitor to fail, verify failure is reported..")
 			test.add_cmd("-c register_rsc -r \"test_rsc\" -C systemd -T lrmd_dummy_daemon -t 1000 "
 				     "-l \"NEW_EVENT event_type:register rsc_id:test_rsc action:none rc:ok op_status:complete\" ")
 			test.add_cmd("-c exec -r \"test_rsc\" -a \"start\" -t 1000 "
 				     "-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:start rc:ok op_status:complete\" ")
 			test.add_cmd("-c exec -r \"test_rsc\" -a \"start\" -t 1000 "
 				     "-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:start rc:ok op_status:complete\" ")
 			test.add_cmd("-c exec -r \"test_rsc\" -a \"monitor\" -i \"100\" -t 1000 "
 				     "-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:monitor rc:ok op_status:complete\" ")
 			test.add_cmd("-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:monitor rc:ok op_status:complete\" -t 2000")
 			test.add_cmd("-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:monitor rc:ok op_status:complete\" -t 2000")
 			test.add_cmd_and_kill("killall -9 -q lrmd_dummy_daemon", "-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:monitor rc:not running op_status:complete\" -t 5000")
 			test.add_cmd("-c cancel -r \"test_rsc\" -a \"monitor\" -i \"100\" -t \"1000\" "
 				     "-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:monitor rc:not running op_status:Cancelled\" ")
 			test.add_expected_fail_cmd("-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:monitor rc:not running op_status:complete\" -t 1000")
 			test.add_expected_fail_cmd("-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:monitor rc:ok op_status:complete\" -t 1000")
 			test.add_cmd("-c unregister_rsc -r \"test_rsc\" -t 1000 "
 				     "-l \"NEW_EVENT event_type:unregister rsc_id:test_rsc action:none rc:ok op_status:complete\" ")
 
 		### monitor fail for upstart resource ###
 		if "upstart" in self.rsc_classes:
 			test = self.new_test("monitor_fail_upstart", "Force upstart monitor to fail, verify failure is reported..")
 			test.add_cmd("-c register_rsc -r \"test_rsc\" -C upstart -T lrmd_dummy_daemon -t 1000 "
 				     "-l \"NEW_EVENT event_type:register rsc_id:test_rsc action:none rc:ok op_status:complete\" ")
 			test.add_cmd("-c exec -r \"test_rsc\" -a \"start\" -t 1000 "
 				     "-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:start rc:ok op_status:complete\" ")
 			test.add_cmd("-c exec -r \"test_rsc\" -a \"start\" -t 1000 "
 				     "-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:start rc:ok op_status:complete\" ")
 			test.add_cmd("-c exec -r \"test_rsc\" -a \"monitor\" -i \"100\" -t 1000 "
 				     "-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:monitor rc:ok op_status:complete\" ")
 			test.add_cmd("-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:monitor rc:ok op_status:complete\" -t 2000")
 			test.add_cmd("-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:monitor rc:ok op_status:complete\" -t 2000")
 			test.add_cmd_and_kill("killall -9 -q dd", "-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:monitor rc:not running op_status:complete\" -t 5000")
 			test.add_cmd("-c cancel -r \"test_rsc\" -a \"monitor\" -i \"100\" -t \"1000\" "
 				     "-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:monitor rc:not running op_status:Cancelled\" ")
 			test.add_expected_fail_cmd("-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:monitor rc:not running op_status:complete\" -t 1000")
 			test.add_expected_fail_cmd("-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:monitor rc:ok op_status:complete\" -t 1000")
 			test.add_cmd("-c unregister_rsc -r \"test_rsc\" -t 1000 "
 				     "-l \"NEW_EVENT event_type:unregister rsc_id:test_rsc action:none rc:ok op_status:complete\" ")
 
 		### Cancel non-existent operation on a resource ###
 		test = self.new_test("cancel_non_existent_op", "Attempt to cancel the wrong monitor operation, verify expected failure")
 		test.add_cmd("-c register_rsc -r \"test_rsc\" -C \"ocf\" -P \"pacemaker\" -T \"Dummy\" -t 1000 "
 			"-l \"NEW_EVENT event_type:register rsc_id:test_rsc action:none rc:ok op_status:complete\" ")
 		test.add_cmd("-c exec -r \"test_rsc\" -a \"start\" -t 1000 "
 			"-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:start rc:ok op_status:complete\" ")
 		test.add_cmd("-c exec -r \"test_rsc\" -a \"start\" -t 1000 "
 			"-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:start rc:ok op_status:complete\" ")
 		test.add_cmd("-c exec -r \"test_rsc\" -a \"monitor\" -i \"100\" -t 1000 "
 			"-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:monitor rc:ok op_status:complete\" ")
 		test.add_cmd("-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:monitor rc:ok op_status:complete\" -t 2000")
 		test.add_expected_fail_cmd("-c cancel -r test_rsc -a \"monitor\" -i 1234 -t \"1000\" " ### interval is wrong, should fail
 			"-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:monitor rc:not running op_status:Cancelled\" ")
 		test.add_expected_fail_cmd("-c cancel -r test_rsc -a stop -i 100 -t \"1000\" " ### action name is wrong, should fail
 			"-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:monitor rc:not running op_status:Cancelled\" ")
 		test.add_cmd("-c unregister_rsc -r \"test_rsc\" -t 1000 "
 			"-l \"NEW_EVENT event_type:unregister rsc_id:test_rsc action:none rc:ok op_status:complete\" ")
 
 		### Attempt to invoke non-existent rsc id ###
 		test = self.new_test("invoke_non_existent_rsc", "Attempt to perform operations on a non-existent rsc id.")
 		test.add_expected_fail_cmd("-c exec -r \"test_rsc\" -a \"start\" -t 1000 "
 			"-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:start rc:unknown error op_status:complete\" ")
 		test.add_expected_fail_cmd("-c exec -r test_rsc -a stop -t 1000"
 			"-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:stop rc:ok op_status:complete\" ")
 		test.add_expected_fail_cmd("-c exec -r test_rsc -a monitor -i 1000 -t 1000"
 			"-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:monitor rc:ok op_status:complete\" ")
 		test.add_expected_fail_cmd("-c cancel -r test_rsc -a start -t 1000 "
 			"-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:start rc:ok op_status:Cancelled\" ")
 		test.add_cmd("-c unregister_rsc -r \"test_rsc\" -t 1000 "
 			"-l \"NEW_EVENT event_type:unregister rsc_id:test_rsc action:none rc:ok op_status:complete\" ")
 
 		### Register and start a resource that doesn't exist, systemd  ###
 		if "systemd" in self.rsc_classes:
 			test = self.new_test("start_uninstalled_systemd", "Register uninstalled systemd agent, try to start, verify expected failure")
 			test.add_cmd("-c register_rsc -r \"test_rsc\" -C systemd -T this_is_fake1234 -t 1000 "
 				     "-l \"NEW_EVENT event_type:register rsc_id:test_rsc action:none rc:ok op_status:complete\" ")
 			test.add_cmd("-c exec -r \"test_rsc\" -a \"start\" -t 1000 "
 				     "-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:start rc:not installed op_status:complete\" ")
 			test.add_cmd("-c unregister_rsc -r \"test_rsc\" -t 1000 "
 				     "-l \"NEW_EVENT event_type:unregister rsc_id:test_rsc action:none rc:ok op_status:complete\" ")
 
 		if "upstart" in self.rsc_classes:
 			test = self.new_test("start_uninstalled_upstart", "Register uninstalled upstart agent, try to start, verify expected failure")
 			test.add_cmd("-c register_rsc -r \"test_rsc\" -C upstart -T this_is_fake1234 -t 1000 "
 				     "-l \"NEW_EVENT event_type:register rsc_id:test_rsc action:none rc:ok op_status:complete\" ")
 			test.add_cmd("-c exec -r \"test_rsc\" -a \"start\" -t 1000 "
 				     "-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:start rc:not installed op_status:complete\" ")
 			test.add_cmd("-c unregister_rsc -r \"test_rsc\" -t 1000 "
 				     "-l \"NEW_EVENT event_type:unregister rsc_id:test_rsc action:none rc:ok op_status:complete\" ")
 
 		### Register and start a resource that doesn't exist, ocf ###
 		test = self.new_test("start_uninstalled_ocf", "Register uninstalled ocf agent, try to start, verify expected failure.")
 		test.add_cmd("-c register_rsc -r \"test_rsc\" -C ocf -P pacemaker -T this_is_fake1234 -t 1000 "
 			"-l \"NEW_EVENT event_type:register rsc_id:test_rsc action:none rc:ok op_status:complete\" ")
 		test.add_cmd("-c exec -r \"test_rsc\" -a \"start\" -t 1000 "
 			"-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:start rc:not installed op_status:complete\" ")
 		test.add_cmd("-c unregister_rsc -r \"test_rsc\" -t 1000 "
 			"-l \"NEW_EVENT event_type:unregister rsc_id:test_rsc action:none rc:ok op_status:complete\" ")
 
 		### Register ocf with non-existent provider  ###
 		test = self.new_test("start_ocf_bad_provider", "Register ocf agent with a non-existent provider, verify expected failure.")
 		test.add_cmd("-c register_rsc -r \"test_rsc\" -C ocf -P pancakes -T Dummy -t 1000 "
 			"-l \"NEW_EVENT event_type:register rsc_id:test_rsc action:none rc:ok op_status:complete\" ")
 		test.add_cmd("-c exec -r \"test_rsc\" -a \"start\" -t 1000 "
 			"-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:start rc:not installed op_status:complete\" ")
 		test.add_cmd("-c unregister_rsc -r \"test_rsc\" -t 1000 "
 			"-l \"NEW_EVENT event_type:unregister rsc_id:test_rsc action:none rc:ok op_status:complete\" ")
 
 		### Register ocf with empty provider field  ###
 		test = self.new_test("start_ocf_no_provider", "Register ocf agent with a no provider, verify expected failure.")
 		test.add_expected_fail_cmd("-c register_rsc -r \"test_rsc\" -C ocf -T Dummy -t 1000 "
 			"-l \"NEW_EVENT event_type:register rsc_id:test_rsc action:none rc:ok op_status:complete\" ")
 		test.add_expected_fail_cmd("-c exec -r \"test_rsc\" -a \"start\" -t 1000 "
 			"-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:start rc:ok op_status:Error\" ")
 		test.add_cmd("-c unregister_rsc -r \"test_rsc\" -t 1000 "
 			"-l \"NEW_EVENT event_type:unregister rsc_id:test_rsc action:none rc:ok op_status:complete\" ")
 
 	### These are tests that target specific cases ###
 	def build_custom_tests(self):
 		### start delay then stop test ###
 		test = self.new_test("start_delay", "Verify start delay works as expected.")
 		test.add_cmd("-c register_rsc -r test_rsc -P pacemaker -C ocf -T Dummy "
 			"-l \"NEW_EVENT event_type:register rsc_id:test_rsc action:none rc:ok op_status:complete\" -t 1000")
 		test.add_cmd("-c exec -r test_rsc -s 2000 -a start -w -t 1000")
 		test.add_expected_fail_cmd("-l "
 			"\"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:start rc:ok op_status:complete\" -t 1000")
 		test.add_cmd("-l "
 			"\"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:start rc:ok op_status:complete\" -t 3000")
 		test.add_cmd("-c exec -r test_rsc -a stop -t 1000"
 			"-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:stop rc:ok op_status:complete\" ")
 		test.add_cmd("-c unregister_rsc -r test_rsc -t 1000 "
 			"-l \"NEW_EVENT event_type:unregister rsc_id:test_rsc action:none rc:ok op_status:complete\" ")
 
 		### start delay, but cancel before it gets a chance to start.  ###
 		test = self.new_test("start_delay_cancel", "Using start_delay, start a rsc, but cancel the start op before execution.")
 		test.add_cmd("-c register_rsc -r test_rsc -P pacemaker -C ocf -T Dummy "
 			"-l \"NEW_EVENT event_type:register rsc_id:test_rsc action:none rc:ok op_status:complete\" -t 1000")
 		test.add_cmd("-c exec -r test_rsc -s 2000 -a start -w -t 1000")
 		test.add_cmd("-c cancel -r test_rsc -a start -t 1000 "
 			"-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:start rc:ok op_status:Cancelled\" ")
 		test.add_expected_fail_cmd("-l "
 			"\"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:start rc:ok op_status:complete\" -t 3000")
 		test.add_cmd("-c unregister_rsc -r test_rsc -t 1000 "
 			"-l \"NEW_EVENT event_type:unregister rsc_id:test_rsc action:none rc:ok op_status:complete\" ")
 
 		### Register a bunch of resources, verify we can get info on them ###
 		test = self.new_test("verify_get_rsc_info", "Register multiple resources, verify retrieval of rsc info.")
 		if "systemd" in self.rsc_classes:
 			test.add_cmd("-c register_rsc -r rsc1 -C systemd -T lrmd_dummy_daemon -t 1000 ")
 			test.add_cmd("-c get_rsc_info -r rsc1 ")
 			test.add_cmd("-c unregister_rsc -r rsc1 -t 1000 ")
 			test.add_expected_fail_cmd("-c get_rsc_info -r rsc1 ")
 
 		if "upstart" in self.rsc_classes:
 			test.add_cmd("-c register_rsc -r rsc1 -C upstart -T lrmd_dummy_daemon -t 1000 ")
 			test.add_cmd("-c get_rsc_info -r rsc1 ")
 			test.add_cmd("-c unregister_rsc -r rsc1 -t 1000 ")
 			test.add_expected_fail_cmd("-c get_rsc_info -r rsc1 ")
 
 		test.add_cmd("-c register_rsc -r rsc2 -C ocf -T Dummy -P pacemaker -t 1000 ")
 		test.add_cmd("-c get_rsc_info -r rsc2 ")
 		test.add_cmd("-c unregister_rsc -r rsc2 -t 1000 ")
 		test.add_expected_fail_cmd("-c get_rsc_info -r rsc2 ")
 
+		### Register duplicate, verify only one entry exists and can still be removed.
+		test = self.new_test("duplicate_registration", "Register resource multiple times, verify only one entry exists and can be removed.")
+		test.add_cmd("-c register_rsc -r rsc2 -C ocf -T Dummy -P pacemaker -t 1000 ")
+		test.add_cmd_check_stdout("-c get_rsc_info -r rsc2 ", "id:rsc2 class:ocf provider:pacemaker type:Dummy")
+		test.add_cmd("-c register_rsc -r rsc2 -C ocf -T Dummy -P pacemaker -t 1000 ")
+		test.add_cmd_check_stdout("-c get_rsc_info -r rsc2 ", "id:rsc2 class:ocf provider:pacemaker type:Dummy")
+		test.add_cmd("-c register_rsc -r rsc2 -C ocf -T Stateful -P pacemaker -t 1000 ")
+		test.add_cmd_check_stdout("-c get_rsc_info -r rsc2 ", "id:rsc2 class:ocf provider:pacemaker type:Stateful")
+		test.add_cmd("-c unregister_rsc -r rsc2 -t 1000 ")
+		test.add_expected_fail_cmd("-c get_rsc_info -r rsc2 ")
+
+		### verify the option to only send notification to the original client. ###
+		test = self.new_test("notify_orig_client_only", "Verify option to only send notifications to the client originating the action.")
+		test.add_cmd("-c register_rsc -r \"test_rsc\" -C \"ocf\" -P \"pacemaker\" -T \"Dummy\" -t 1000 "
+			"-l \"NEW_EVENT event_type:register rsc_id:test_rsc action:none rc:ok op_status:complete\" ")
+		test.add_cmd("-c exec -r \"test_rsc\" -a \"start\" -t 1000 "
+			"-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:start rc:ok op_status:complete\" ")
+		test.add_cmd("-c exec -r \"test_rsc\" -a \"monitor\" -i \"100\" -t 1000 -n "
+			"-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:monitor rc:ok op_status:complete\" ")
+		# this will fail because the monitor notifications should only go to the original caller, which no longer exists. 
+		test.add_expected_fail_cmd("-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:monitor rc:ok op_status:complete\" -t 2000")
+		test.add_cmd("-c cancel -r \"test_rsc\" -a \"monitor\" -i \"100\" -t \"1000\" ")
+		test.add_cmd("-c unregister_rsc -r \"test_rsc\" -t 1000 "
+			"-l \"NEW_EVENT event_type:unregister rsc_id:test_rsc action:none rc:ok op_status:complete\" ")
+
 		### get metadata ###
 		test = self.new_test("get_ocf_metadata", "Retrieve metadata for a resource")
 		test.add_cmd_check_stdout("-c metadata -C \"ocf\" -P \"pacemaker\" -T \"Dummy\""
 			,"resource-agent name=\"Dummy\"")
 		test.add_cmd("-c metadata -C \"ocf\" -P \"pacemaker\" -T \"Stateful\"")
 		test.add_expected_fail_cmd("-c metadata -P \"pacemaker\" -T \"Stateful\"")
 		test.add_expected_fail_cmd("-c metadata -C \"ocf\" -P \"pacemaker\" -T \"fake_agent\"")
 
 		### get metadata ###
 		test = self.new_test("get_lsb_metadata", "Retrieve metadata for a resource")
 		test.add_cmd_check_stdout("-c metadata -C \"lsb\" -T \"LSBDummy\""
 			,"resource-agent name=\"LSBDummy\"")
 
 		### get stonith metadata ###
 		test = self.new_test("get_stonith_metadata", "Retrieve stonith metadata for a resource")
 		test.add_cmd_check_stdout("-c metadata -C \"stonith\" -P \"pacemaker\" -T \"fence_dummy_monitor\"",
 			"resource-agent name=\"fence_dummy_monitor\"")
 
+		### get metadata ###
+		if "systemd" in self.rsc_classes:
+			test = self.new_test("get_systemd_metadata", "Retrieve metadata for a resource")
+			test.add_cmd_check_stdout("-c metadata -C \"systemd\" -T \"lrmd_dummy_daemon\""
+				,"resource-agent name=\"lrmd_dummy_daemon\"")
+
+		### get metadata ###
+		if "upstart" in self.rsc_classes:
+			test = self.new_test("get_systemd_metadata", "Retrieve metadata for a resource")
+			test.add_cmd_check_stdout("-c metadata -C \"systemd\" -T \"lrmd_dummy_daemon\""
+				,"resource-agent name=\"lrmd_dummy_daemon\"")
+
 		### get ocf providers  ###
 		test = self.new_test("list_ocf_providers", "Retrieve list of available resource providers, verifies pacemaker is a provider.")
 		test.add_cmd_check_stdout("-c list_ocf_providers ", "pacemaker")
 		test.add_cmd_check_stdout("-c list_ocf_providers -T ping", "pacemaker")
 
 		### Verify agents only exist in their lists ###
 		test = self.new_test("verify_agent_lists", "Verify the agent lists contain the right data.")
 		test.add_cmd_check_stdout("-c list_agents ", "Stateful")                                  ### ocf ###
 		test.add_cmd_check_stdout("-c list_agents -C ocf", "Stateful")
 		test.add_cmd_check_stdout("-c list_agents -C lsb", "", "Stateful")                        ### should not exist
 		test.add_cmd_check_stdout("-c list_agents -C service", "", "Stateful")                    ### should not exist
 		test.add_cmd_check_stdout("-c list_agents ", "LSBDummy")                                  ### init.d ###
 		test.add_cmd_check_stdout("-c list_agents -C lsb", "LSBDummy")
 		test.add_cmd_check_stdout("-c list_agents -C service", "LSBDummy")
 		test.add_cmd_check_stdout("-c list_agents -C ocf", "", "LSBDummy")                        ### should not exist
 
 		test.add_cmd_check_stdout("-c list_agents -C lsb", "", "lrmd_dummy_daemon")               ### should not exist
 		test.add_cmd_check_stdout("-c list_agents -C ocf", "", "lrmd_dummy_daemon")               ### should not exist
 		test.add_cmd_check_stdout("-c list_agents -C lsb", "", "fence_dummy_monitor")             ### should not exist
 		test.add_cmd_check_stdout("-c list_agents -C service", "", "fence_dummy_monitor")         ### should not exist
 		test.add_cmd_check_stdout("-c list_agents -C ocf", "", "fence_dummy_monitor")             ### should not exist
 
 		if "systemd" in self.rsc_classes:
 			test.add_cmd_check_stdout("-c list_agents ", "lrmd_dummy_daemon")                 ### systemd ###
 			test.add_cmd_check_stdout("-c list_agents -C service", "lrmd_dummy_daemon")
 			test.add_cmd_check_stdout("-c list_agents -C systemd", "", "Stateful")		  ### should not exist
 			test.add_cmd_check_stdout("-c list_agents -C systemd", "lrmd_dummy_daemon")
 			test.add_cmd_check_stdout("-c list_agents -C systemd", "", "fence_dummy_monitor") ### should not exist
 			test.add_cmd_check_stdout("-c list_agents -C systemd", "", "LSBDummy")            ### should not exist
 
 		if "upstart" in self.rsc_classes:
 			test.add_cmd_check_stdout("-c list_agents ", "lrmd_dummy_daemon")                 ### upstart ###
 			test.add_cmd_check_stdout("-c list_agents -C service", "lrmd_dummy_daemon")
 			test.add_cmd_check_stdout("-c list_agents -C upstart", "", "Stateful")		  ### should not exist
 			test.add_cmd_check_stdout("-c list_agents -C upstart", "lrmd_dummy_daemon")
 			test.add_cmd_check_stdout("-c list_agents -C upstart", "", "fence_dummy_monitor") ### should not exist
 			test.add_cmd_check_stdout("-c list_agents -C upstart", "", "LSBDummy")            ### should not exist
 
 		if "stonith" in self.rsc_classes:
 			test.add_cmd_check_stdout("-c list_agents -C stonith", "", "LSBDummy")            ### should not exist
 			test.add_cmd_check_stdout("-c list_agents -C stonith", "fence_dummy_monitor")     ### stonith ###
 			test.add_cmd_check_stdout("-c list_agents -C stonith", "", "lrmd_dummy_daemon")   ### should not exist
 			test.add_cmd_check_stdout("-c list_agents -C stonith", "", "Stateful")            ### should not exist
 			test.add_cmd_check_stdout("-c list_agents ", "fence_dummy_monitor")
 
 	def print_list(self):
 		print "\n==== %d TESTS FOUND ====" % (len(self.tests))
 		print "%35s - %s" % ("TEST NAME", "TEST DESCRIPTION")
 		print "%35s - %s" % ("--------------------", "--------------------")
 		for test in self.tests:
 			print "%35s - %s" % (test.name, test.description)
 		print "==== END OF LIST ====\n"
 
 	def run_single(self, name):
 		for test in self.tests:
 			if test.name == name:
 				test.run()
 				break;
 
 	def run_tests_matching(self, pattern):
 		for test in self.tests:
 			if test.name.count(pattern) != 0:
 				test.run()
 
 	def run_tests(self):
 		for test in self.tests:
 			test.run()
 
 	def print_results(self):
 		failures = 0;
 		success = 0;
 		print "\n\n======= FINAL RESULTS =========="
 		print "\n--- FAILURE RESULTS:"
 		for test in self.tests:
 			if test.executed == 0:
 				continue
 
 			if test.get_exitcode() != 0:
 				failures = failures + 1
 				test.print_result("    ")
 			else:
 				success = success + 1
 
 		if failures == 0:
 			print "    None"
 
 		print "\n--- TOTALS\n    Pass:%d\n    Fail:%d\n" % (success, failures)
 
 class TestOptions:
 	def __init__(self):
 		self.options = {}
 		self.options['list-tests'] = 0
 		self.options['run-all'] = 1
 		self.options['run-only'] = ""
 		self.options['run-only-pattern'] = ""
 		self.options['verbose'] = 0
 		self.options['invalid-arg'] = ""
 		self.options['show-usage'] = 0
 
 	def build_options(self, argv):
 		args = argv[1:]
 		skip = 0
 		for i in range(0, len(args)):
 			if skip:
 				skip = 0
 				continue
 			elif args[i] == "-h" or args[i] == "--help":
 				self.options['show-usage'] = 1
 			elif args[i] == "-l" or args[i] == "--list-tests":
 				self.options['list-tests'] = 1
 			elif args[i] == "-V" or args[i] == "--verbose":
 				self.options['verbose'] = 1
 			elif args[i] == "-r" or args[i] == "--run-only":
 				self.options['run-only'] = args[i+1]
 				skip = 1
 			elif args[i] == "-p" or args[i] == "--run-only-pattern":
 				self.options['run-only-pattern'] = args[i+1]
 				skip = 1
 
 	def show_usage(self):
 		print "usage: " + sys.argv[0] + " [options]"
 		print "If no options are provided, all tests will run"
 		print "Options:"
 		print "\t [--help | -h]                        Show usage"
 		print "\t [--list-tests | -l]                  Print out all registered tests."
 		print "\t [--run-only | -r 'testname']         Run a specific test"
 		print "\t [--verbose | -V]                     Verbose output"
 		print "\t [--run-only-pattern | -p 'string']   Run only tests containing the string value"
 		print "\n\tExample: Run only the test 'start_top'"
 		print "\t\t python ./regression.py --run-only start_stop"
 		print "\n\tExample: Run only the tests with the string 'systemd' present in them"
 		print "\t\t python ./regression.py --run-only-pattern systemd"
 
 
 def main(argv):
 	o = TestOptions()
 	o.build_options(argv)
 
 	tests = Tests(o.options['verbose'])
 
 	tests.build_generic_tests()
 	tests.build_multi_rsc_tests()
 	tests.build_negative_tests()
 	tests.build_custom_tests()
 
 	tests.setup_test_environment()
 
 	print "Starting ..."
 
 	if o.options['list-tests']:
 		tests.print_list()
 	elif o.options['show-usage']:
 		o.show_usage()
 	elif o.options['run-only-pattern'] != "":
 		tests.run_tests_matching(o.options['run-only-pattern'])
 		tests.print_results()
 	elif o.options['run-only'] != "":
 		tests.run_single(o.options['run-only'])
 		tests.print_results()
 	else:
 		tests.run_tests()
 		tests.print_results()
 
 	tests.cleanup_test_environment()
 
 if __name__=="__main__":
 	main(sys.argv)
diff --git a/lrmd/test.c b/lrmd/test.c
index c09b1f53f5..15dc6916a4 100644
--- a/lrmd/test.c
+++ b/lrmd/test.c
@@ -1,565 +1,577 @@
 /*
  * Copyright (c) 2012 David Vossel <dvossel@redhat.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2.1 of the License, or (at your option) any later version.
  * 
  * This library 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
  * Lesser General Public License for more details.
  * 
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  *
  */
 
 #include <crm_internal.h>
 
 #include <glib.h>
 #include <unistd.h>
 
 #include <crm/crm.h>
 #include <crm/services.h>
 #include <crm/common/mainloop.h>
 
 #include <crm/pengine/status.h>
 #include <crm/cib.h>
 #include <crm/lrmd.h>
 
 /* *INDENT-OFF* */
 static struct crm_option long_options[] = {
     {"help",             0, 0, '?'},
     {"verbose",          0, 0, 'V', "\t\tPrint out logs and events to screen"},
     {"quiet",            0, 0, 'Q', "\t\tSuppress all output to screen"},
     {"listen",           1, 0, 'l', "\tListen for a specific event string"},
     {"api-call",         1, 0, 'c', "\tDirectly relates to lrmd api functions"},
     {"no-wait",          0, 0, 'w', "\tMake api call and do not wait for result."},
     {"is-running",       0, 0, 'R', "\tDetermine if a resource is registered and running."},
+    {"notify-orig",      0, 0, 'n', "\tOnly notify this client the results of an api action."},
     {"-spacer-",         1, 0, '-', "\nParameters for api-call option"},
     {"action",           1, 0, 'a'},
     {"rsc-id",           1, 0, 'r'},
     {"cancel-call-id",   1, 0, 'x'},
     {"provider",         1, 0, 'P'},
     {"class",            1, 0, 'C'},
     {"type",             1, 0, 'T'},
     {"interval",         1, 0, 'i'},
     {"timeout",          1, 0, 't'},
     {"start-delay",      1, 0, 's'},
     {"param-key",        1, 0, 'k'},
     {"param-val",        1, 0, 'v'},
     
     {"-spacer-",         1, 0, '-'},
     {0, 0, 0, 0}
 };
 /* *INDENT-ON* */
 
 cib_t *cib_conn = NULL;
 static int exec_call_id = 0;
 static int exec_call_opts = 0;
 extern void cleanup_alloc_calculations(pe_working_set_t * data_set);
 
 static struct {
     int verbose;
     int quiet;
     int print;
     int interval;
     int timeout;
     int start_delay;
     int cancel_call_id;
     int no_wait;
     int is_running;
     int no_connect;
     const char *api_call;
     const char *rsc_id;
     const char *provider;
     const char *class;
     const char *type;
     const char *action;
     const char *listen;
     lrmd_key_value_t *params;
 } options;
 
 GMainLoop *mainloop = NULL;
 lrmd_t *lrmd_conn = NULL;
 
 static char event_buf_v0[1024];
 
+static void
+test_exit(int rc)
+{
+    lrmd_api_delete(lrmd_conn);
+    exit(rc);
+}
+
 #define print_result(result) \
     if (!options.quiet) {    \
         result;              \
     }                        \
-    
+
 #define report_event(event)                                             \
     snprintf(event_buf_v0, sizeof(event_buf_v0), "NEW_EVENT event_type:%s rsc_id:%s action:%s rc:%s op_status:%s", \
              lrmd_event_type2str(event->type),                          \
              event->rsc_id,                                             \
              event->op_type ? event->op_type : "none",                  \
              lrmd_event_rc2str(event->rc),                              \
              services_lrm_status_str(event->op_status));                \
     crm_info("%s", event_buf_v0);;
 
 static void
 test_shutdown(int nsig)
 {
     lrmd_api_delete(lrmd_conn);
+    lrmd_conn = NULL;
 }
 
 static void
 read_events(lrmd_event_data_t * event)
 {
     report_event(event);
     if (options.listen) {
         if (safe_str_eq(options.listen, event_buf_v0)) {
             print_result(printf("LISTEN EVENT SUCCESSFUL\n"));
-            exit(0);
+            test_exit(0);
         }
     }
 
     if (exec_call_id && (event->call_id == exec_call_id)) {
         if (event->op_status == 0 && event->rc == 0) {
             print_result(printf("API-CALL SUCCESSFUL for 'exec'\n"));
         } else {
             print_result(printf("API-CALL FAILURE for 'exec', rc:%d lrmd_op_status:%s\n",
                                 event->rc, services_lrm_status_str(event->op_status)));
-            exit(-1);
+            test_exit(-1);
         }
 
         if (!options.listen) {
-            exit(0);
+            test_exit(0);
         }
     }
 }
 
 static gboolean
 timeout_err(gpointer data)
 {
     print_result(printf("LISTEN EVENT FAILURE - timeout occurred, never found.\n"));
-    exit(-1);
+    test_exit(-1);
 
     return FALSE;
 }
 
 static void
 try_connect(void)
 {
     int tries = 10;
     int i = 0;
     int rc = 0;
 
     for (i = 0; i < tries; i++) {
         rc = lrmd_conn->cmds->connect(lrmd_conn, "lrmd", NULL);
 
         if (!rc) {
             crm_info("lrmd client connection established");
             return;
         } else {
             crm_info("lrmd client connection failed");
         }
         sleep(1);
     }
 
     print_result(printf("API CONNECTION FAILURE\n"));
-    exit(-1);
+    test_exit(-1);
 }
 
 static gboolean
 start_test(gpointer user_data)
 {
     int rc = 0;
 
     if (!options.no_connect) {
         try_connect();
     }
     lrmd_conn->cmds->set_callback(lrmd_conn, read_events);
 
     if (options.timeout) {
         g_timeout_add(options.timeout, timeout_err, NULL);
     }
 
     if (!options.api_call) {
         return 0;
     }
 
     if (safe_str_eq(options.api_call, "exec")) {
         rc = lrmd_conn->cmds->exec(lrmd_conn,
                                    options.rsc_id,
                                    options.action,
                                    NULL,
                                    options.interval,
                                    options.timeout,
                                    options.start_delay, exec_call_opts, options.params);
 
         if (rc > 0) {
             exec_call_id = rc;
             print_result(printf("API-CALL 'exec' action pending, waiting on response\n"));
         }
 
     } else if (safe_str_eq(options.api_call, "register_rsc")) {
         rc = lrmd_conn->cmds->register_rsc(lrmd_conn,
                                            options.rsc_id,
                                            options.class, options.provider, options.type, 0);
     } else if (safe_str_eq(options.api_call, "get_rsc_info")) {
         lrmd_rsc_info_t *rsc_info;
 
         rsc_info = lrmd_conn->cmds->get_rsc_info(lrmd_conn, options.rsc_id, 0);
 
         if (rsc_info) {
             print_result(printf("RSC_INFO: id:%s class:%s provider:%s type:%s\n",
                                 rsc_info->id, rsc_info->class,
                                 rsc_info->provider ? rsc_info->provider : "<none>",
                                 rsc_info->type));
             lrmd_free_rsc_info(rsc_info);
             rc = pcmk_ok;
         } else {
             rc = -1;
         }
     } else if (safe_str_eq(options.api_call, "unregister_rsc")) {
         rc = lrmd_conn->cmds->unregister_rsc(lrmd_conn, options.rsc_id, 0);
     } else if (safe_str_eq(options.api_call, "cancel")) {
         rc = lrmd_conn->cmds->cancel(lrmd_conn, options.rsc_id, options.action, options.interval);
     } else if (safe_str_eq(options.api_call, "metadata")) {
         char *output = NULL;
 
         rc = lrmd_conn->cmds->get_metadata(lrmd_conn,
                                            options.class,
                                            options.provider, options.type, &output, 0);
         if (rc == pcmk_ok) {
             print_result(printf("%s", output));
             free(output);
         }
     } else if (safe_str_eq(options.api_call, "list_agents")) {
         lrmd_list_t *list = NULL;
         lrmd_list_t *iter = NULL;
 
         rc = lrmd_conn->cmds->list_agents(lrmd_conn, &list, options.class, options.provider);
 
         if (rc > 0) {
             print_result(printf("%d agents found\n", rc));
             for (iter = list; iter != NULL; iter = iter->next) {
                 print_result(printf("%s\n", iter->val));
             }
             lrmd_list_freeall(list);
             rc = 0;
         } else {
             print_result(printf("API_CALL FAILURE - no agents found\n"));
             rc = -1;
         }
     } else if (safe_str_eq(options.api_call, "list_ocf_providers")) {
         lrmd_list_t *list = NULL;
         lrmd_list_t *iter = NULL;
 
         rc = lrmd_conn->cmds->list_ocf_providers(lrmd_conn, options.type, &list);
 
         if (rc > 0) {
             print_result(printf("%d providers found\n", rc));
             for (iter = list; iter != NULL; iter = iter->next) {
                 print_result(printf("%s\n", iter->val));
             }
             lrmd_list_freeall(list);
             rc = 0;
         } else {
             print_result(printf("API_CALL FAILURE - no providers found\n"));
             rc = -1;
         }
 
     } else if (safe_str_eq(options.api_call, "list_standards")) {
         lrmd_list_t *list = NULL;
         lrmd_list_t *iter = NULL;
 
         rc = lrmd_conn->cmds->list_standards(lrmd_conn, &list);
 
         if (rc > 0) {
             print_result(printf("%d standards found\n", rc));
             for (iter = list; iter != NULL; iter = iter->next) {
                 print_result(printf("%s\n", iter->val));
             }
             lrmd_list_freeall(list);
             rc = 0;
         } else {
             print_result(printf("API_CALL FAILURE - no providers found\n"));
             rc = -1;
         }
 
     } else if (options.api_call) {
         print_result(printf("API-CALL FAILURE unknown action '%s'\n", options.action));
-        exit(-1);
+        test_exit(-1);
     }
 
     if (rc < 0) {
         print_result(printf("API-CALL FAILURE for '%s' api_rc:%d\n", options.api_call, rc));
-        exit(-1);
+        test_exit(-1);
     }
 
     if (options.api_call && rc == pcmk_ok) {
         print_result(printf("API-CALL SUCCESSFUL for '%s'\n", options.api_call));
         if (!options.listen) {
-            exit(0);
+            test_exit(0);
         }
     }
 
     if (options.no_wait) {
         /* just make the call and exit regardless of anything else. */
-        exit(0);
+        test_exit(0);
     }
 
     return 0;
 }
 
 static resource_t *
 find_rsc_or_clone(const char *rsc, pe_working_set_t * data_set)
 {
     resource_t *the_rsc = pe_find_resource(data_set->resources, rsc);
 
     if (the_rsc == NULL) {
         char *as_clone = crm_concat(rsc, "0", ':');
 
         the_rsc = pe_find_resource(data_set->resources, as_clone);
         free(as_clone);
     }
     return the_rsc;
 }
 
 static int
 generate_params(void)
 {
     int rc = 0;
     pe_working_set_t data_set;
     xmlNode *cib_xml_copy = NULL;
     resource_t *rsc = NULL;
     GHashTable *params = NULL;
     GHashTable *meta = NULL;
     GHashTableIter iter;
 
     if (options.params) {
         return 0;
     }
 
     set_working_set_defaults(&data_set);
 
     cib_conn = cib_new();
     rc = cib_conn->cmds->signon(cib_conn, "lrmd_test", cib_query);
     if (rc != pcmk_ok) {
         crm_err("Error signing on to the CIB service: %s\n", pcmk_strerror(rc));
         rc = -1;
         goto param_gen_bail;
     }
 
     cib_xml_copy = get_cib_copy(cib_conn);
 
     if (!cib_xml_copy) {
         crm_err("Error retrieving cib copy.");
         rc = -1;
         goto param_gen_bail;
     }
 
     if (cli_config_update(&cib_xml_copy, NULL, FALSE) == FALSE) {
         crm_err("Error updating cib configuration");
         rc = -1;
         goto param_gen_bail;
     }
 
     data_set.input = cib_xml_copy;
     data_set.now = new_ha_date(TRUE);
 
     cluster_status(&data_set);
     if (options.rsc_id) {
         rsc = find_rsc_or_clone(options.rsc_id, &data_set);
     }
 
     if (!rsc) {
         crm_err("Resource does not exist in config");
         rc = -1;
         goto param_gen_bail;
     }
 
     params = g_hash_table_new_full(crm_str_hash,
                                    g_str_equal, g_hash_destroy_str, g_hash_destroy_str);
     meta = g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str);
 
     get_rsc_attributes(params, rsc, NULL, &data_set);
     get_meta_attributes(meta, rsc, NULL, &data_set);
 
     if (params) {
         char *key = NULL;
         char *value = NULL;
 
         g_hash_table_iter_init(&iter, params);
         while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & value)) {
             options.params = lrmd_key_value_add(options.params, key, value);
         }
         g_hash_table_destroy(params);
     }
 
     if (meta) {
         char *key = NULL;
         char *value = NULL;
 
         g_hash_table_iter_init(&iter, meta);
         while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & value)) {
             char *crm_name = crm_meta_name(key);
 
             options.params = lrmd_key_value_add(options.params, crm_name, value);
             free(crm_name);
         }
         g_hash_table_destroy(meta);
     }
 
   param_gen_bail:
 
     cleanup_alloc_calculations(&data_set);
     return rc;
 }
 
 int
 main(int argc, char **argv)
 {
     int option_index = 0;
     int argerr = 0;
     int flag;
     char *key = NULL;
     char *val = NULL;
     crm_trigger_t *trig;
 
     crm_set_options(NULL, "mode [options]", long_options,
                     "Inject commands into the lrmd and watch for events\n");
 
     while (1) {
         flag = crm_get_option(argc, argv, &option_index);
         if (flag == -1)
             break;
 
         switch (flag) {
             case '?':
                 crm_help(flag, EX_OK);
                 break;
             case 'V':
                 options.verbose = 1;
                 break;
             case 'Q':
                 options.quiet = 1;
                 options.verbose = 0;
                 break;
             case 'l':
                 options.listen = optarg;
                 break;
             case 'w':
                 options.no_wait = 1;
                 break;
             case 'R':
                 options.is_running = 1;
                 break;
+            case 'n':
+                exec_call_opts = lrmd_opt_notify_orig_only;
+                break;
             case 'c':
                 options.api_call = optarg;
                 break;
             case 'a':
                 options.action = optarg;
                 break;
             case 'r':
                 options.rsc_id = optarg;
                 break;
             case 'x':
                 options.cancel_call_id = atoi(optarg);
                 break;
             case 'P':
                 options.provider = optarg;
                 break;
             case 'C':
                 options.class = optarg;
                 break;
             case 'T':
                 options.type = optarg;
                 break;
             case 'i':
                 options.interval = atoi(optarg);
                 break;
             case 't':
                 options.timeout = atoi(optarg);
                 break;
             case 's':
                 options.start_delay = atoi(optarg);
                 break;
             case 'k':
                 key = optarg;
                 if (key && val) {
                     options.params = lrmd_key_value_add(options.params, key, val);
                     key = val = NULL;
                 }
                 break;
             case 'v':
                 val = optarg;
                 if (key && val) {
                     options.params = lrmd_key_value_add(options.params, key, val);
                     key = val = NULL;
                 }
                 break;
             default:
                 ++argerr;
                 break;
         }
     }
 
     if (argerr) {
         crm_help('?', EX_USAGE);
     }
     if (optind > argc) {
         ++argerr;
     }
 
     if (!options.listen &&
         (safe_str_eq(options.api_call, "metadata") ||
          safe_str_eq(options.api_call, "list_agents") ||
          safe_str_eq(options.api_call, "list_standards") ||
          safe_str_eq(options.api_call, "list_ocf_providers"))) {
         options.no_connect = 1;
     }
 
     crm_log_init("lrmd_ctest", LOG_INFO, TRUE, options.verbose ? TRUE : FALSE, argc, argv, FALSE);
 
     if (options.is_running) {
         if (!options.timeout) {
             options.timeout = 30000;
         }
         options.interval = 0;
         if (!options.rsc_id) {
             crm_err("rsc-id must be given when is-running is used");
-            exit(-1);
+            test_exit(-1);
         }
 
         if (generate_params()) {
             print_result(printf
                          ("Failed to retrieve rsc parameters from cib, can not determine if rsc is running.\n"));
-            exit(-1);
+            test_exit(-1);
         }
         options.api_call = "exec";
         options.action = "monitor";
         exec_call_opts = lrmd_opt_notify_orig_only;
     }
 
     /* if we can't perform an api_call or listen for events, 
      * there is nothing to do */
     if (!options.api_call && !options.listen) {
         crm_err("Nothing to be done.  Please specify 'api-call' and/or 'listen'");
         return 0;
     }
 
     lrmd_conn = lrmd_api_new();
     trig = mainloop_add_trigger(G_PRIORITY_HIGH, start_test, NULL);
     mainloop_set_trigger(trig);
     mainloop_add_signal(SIGTERM, test_shutdown);
 
     crm_info("Starting");
     mainloop = g_main_new(FALSE);
     g_main_run(mainloop);
-    lrmd_api_delete(lrmd_conn);
 
     if (cib_conn != NULL) {
         cib_conn->cmds->signoff(cib_conn);
         cib_delete(cib_conn);
     }
 
+    test_exit(0);
     return 0;
 }