File pacemaker_starter.py of Package pacemaker-mgmt

#!/usr/bin/python

import sys, os
try:
	import pygtk
	pygtk.require("2.0")
except:
	pass

try:
	import gtk
	import gtk.glade
except:
	print "Failed to import gtk.glade"
	sys.exit(1)

top_window = None

totem_option_table = {
	"version":{"doc":"The only valid version is 2", 
		   "type":"int",
		   "default_value":2,
		   "suggested_value":2},
	"nodeid":{"doc":"The fixed 32 bit value to indentify node to cluster membership. Optional for IPv4, and required for IPv6. 0 is reserved for other usage", 
		  "type":"int",
		  "default_value":70912},
	"clear_node_high_bit":{"doc":"To make sure the auto-generated nodeid is positive", 
			       "default_value":"yes"},
	"secauth":{"doc":"HMAC/SHA1 should be used to authenticate all message", 
		   "default_value":"off"},
	"rrp_mode":{"doc":"The mode for redundant ring. None is used when only 1 interface specified, otherwise, only active or passive may be choosen", 
		    "type":"select[none,active,passive]", "default_value":"none"},
	"netmtu":{"doc":"Size of MTU", "type":"int", "default_value":1500},
	"threads":{"doc":"How many threads should be used to encypt and sending message. Only have meanings when secauth is turned on", 
		   "type":"int", "default_value":0},
	"vsftype":{"doc":"The virtual synchrony filter type used to indentify a primary component. Change with care.", 
		   "default_value":"ykd",
		   "suggested_value":"none"},
	"token":{"doc":"Timeout for a token lost. in ms", 
		 "type":"int", "default_value":1000,
		 "suggested_value":10000},
	"token_retransmit":{"doc":"How long before receving a token then token is retransmitted. Don't change this value.", 
			    "type":"int", "default_value":238},
	"hold":{"doc":"How long the token should be held by representative when protocol is under low utilization. Don't change this value.", 
		"type":"int", "default_value":180},
	"token_retransmits_before_loss_const":{"doc":"How many token retransmits should be attempted before forming a new configuration.", 
					       "type":"int", "default_value":4,
					       "suggested_value":20},
	"join":{"doc":"How long to wait for join messages in membership protocol. in ms", 
		"type":"int", "default_value":50,
		"suggested_value":60},
	"send_join":{"doc":"This timeout specifies in milliseconds an upper range between 0 and send_join to wait before sending a join message.", 
		     "type":"int", "default_value":0},
	"consensus":{"doc":"How long to wait for consensus to be achieved before starting a new round of membership configuration.", 
		     "type":"int", "default_value":800,
		     "suggested_value":4800},
	"merge":{"doc":"How long to wait before checking for a partition when no multicast traffic is being sent.", 
		 "type":"int", "default_value":200},
	"downcheck":{"doc":"How long to wait before checking that a network interface is back up after it has been downed.", 
		     "type":"int", "default_value":1000},
	"fail_to_recv_const":{"doc":"How many rotations of the token without receiving any of the messages when messages should be received may occur before a new configuration is formed", 
			      "type":"int", "default_value":50},
	"seqno_unchanged_const":{"doc":"How many rotations of the token without any multicast traffic should occur before the merge detection timeout is started.", 
				 "type":"int", "default_value":30},
	"heartbeat_failure_allowed":{"doc":"Configures the optional HeartBeating mechanism for faster failure detection. 0 for disable.", "type":"int", "default_value":0},
	"max_network_delay":{"doc":"The approximate delay that your network takes to transport one packet from one machine to another.", 
			     "type":"int", "default_value":50},
	"window_size":{"doc":"The maximum number of messages that may be sent on one token rotation.", 
		       "type":"int", "default_value":50},
	"max_messages":{"doc":"The maximum number of messages that may be sent by one processor on receipt of the token.", 
			"type":"int", "default_value":17,
			"suggested_value":20},
	"rrp_problem_count_timeout":{"doc":"The time in milliseconds to wait before decrementing the problem count by 1 for a particular ring to ensure a link is not marked faulty for transient network failures.", 
				     "type":"int", "default_value":2000},
	"rrp_problem_count_threshhold":{"doc":"The number of times a problem is detected with a link before setting the link faulty.", 
					"type":"int", "default_value":10},
	"rrp_token_expired_timeout":{"doc":"This specifies the time in milliseconds to increment the problem counter for the redundant ring protocol after not having received a token from all rings for a particular processor.", "type":"int", "default_value":47},
	"keyfile":{"doc":"Location of key file. If not set, it defaults to the environoment OPENAIS_TOTEM_AUTHKEY_FILE, then /etc/ais/authkey", "default_value":"/etc/ais/authkey"},
	"key":{"doc":"The key itself in the configuration file. Should be 128 characters. Dont use the default value", "default_value":"CatchMeeCatchMeeCatchMeeCatchMeeCatchMeeCatchMeeCatchMeeCatchMeeCatchMeeCatchMeeCatchMeeCatchMeeCatchMeeCatchMeeCatchMeeCatchMee"}
}


aisexec_option_table = {
	"user":{"doc":"User to run aisexec as. Needs to be root for Pacemaker", "default_value":"root"}, 
	"group":{"doc":"Group to run aisexec as. Needs to be root for Pacemaker", "default_value":"root"}}

service_option_table = {
	"name":{"doc":"The name of the service", "default_value":"pacemaker"},
	"ver":{"doc":"Version", "default_value":"0"},
	"use_mgmtd":{"doc":"Default to start mgmtd with pacemaker", "default_value":"yes"},
	"expected_nodes":{"doc":"Nodes we expected to see in the cluster", "default_value":2},
	"expected_votes":{"doc":"Votes we expected to see in the cluster", "default_value":2},
	"quorum_votes":{"doc":"Votes needed to have the quorum", "default_value":1},
	"use_logd":{"doc":"Use logd for pacemaker", "default_value":"no"},
}

interface_option_table = {
	"ringnumber":{"doc":"The ringnumber assigned to this interface setting", "default_value":0, "type":"int"},
	"bindnetaddr":{"doc":"Network Address to be bind for this interface setting", "default_value":0},
	"mcastaddr":{"doc":"The multicast address to be used", "default_value":0},
	"mcastport":{"doc":"The multicast port to be used", "default_value":0, "type":"int"},
	}

event_option_table = {
	"delivery_queue_size":{"doc":"The full size of the outgoing  delivery queue to  the application", "default_value":1000},
	"delivery_queue_resume":{"doc":"When new events can be accepted by the event service when the delivery queue count of pending messages has reached this value",
				 "default_value":500},
}

amf_option_table = {
	"mode":{"doc":"Enable or disable AMF ", "default_value":"disable", "suggested_value":"disable"},
}
logging_option_table = {
	"debug":{"doc":"Whether or not turning on the debug information in the log", "default_value":"off",
		 "suggested_value":"off"},
	"fileline":{"doc":"Logging file line in the source code as well", "default_value":"off",
		    "suggested_value":"off"},
	"to_syslog":{"doc":"Log to syslog", "default_value":"yes",
		     "suggested_value":"yes"},
	"to_stderr":{"doc":"Log to the standard error output", "default_value":"yes", "suggested_value":"yes"},
	"to_file":{"doc":"Log to a specified file", "default_value":"no", "suggested_value":"no"},
	"logfile":{"doc":"Log to be saved in this specified file", "default_value":"/tmp/saved_pacemaker_log"},
	"syslog_facility":{"doc":"Facility in syslog", "default_value":"daemon", "suggested_value":"daemon"},
	"timestamp":{"doc":"Log timestamp as well", "default_value":"on", "suggested_value":"on"},
}
logger_option_table = {
	"ident":{"doc":"Ident for the logger, i.e. AMF", "default_value":"AMF"},
	"debug":{"doc":"Enable debug for this logger.", "default_value":"on"},
	"tags":{"doc":"Tags used for this logger.", "default_value":"enter|leave|trace1"},
	}

totem_options = {"interface":[]}
ais_options = {}
pacemaker_service_options = {}
service_options = {}
logging_options = {"logger":[]}
amf_options = {}
event_options = {}

def strip_comments_and_pending_space(line):
	return line.split('#')[0].rstrip()

def get_next_line(ff):
	l = ff.next()
	return strip_comments_and_pending_space(l)

def is_ais_true(s):
	return (s == "true" or s == "on" or s == "yes" or s == "y" or s == "1")

def generate_default_ais_options():
	ais_options["user"] = "root"
	ais_options["group"] = "root"

def fulfill_default_amf_options():
	if amf_options.get("mode", None) == None:
		amf_options["mode"] = "disable"

def fulfill_default_pacemaker_options ():
	pacemaker_service_options["name"] = "pacemaker"
	pacemaker_service_options["ver"] = "0"
	
def fulfill_default_logging_options ():
	for opt in logging_option_table.keys():
		if opt == "logger": continue
		sv = logging_option_table[opt].get("suggested_value", None)
		v = logging_options.get(opt, None)
		if v == None and sv != None:
			logging_options[opt] = sv

def fulfill_suggested_totem_options():
	totem_options["version"] = 2
	for opt in totem_option_table.keys():
		if opt == "interface": continue
		sv = totem_option_table[opt].get("suggested_value", None)
		v = totem_options.get(opt, None)
		if v == None and sv != None:
			totem_options[opt] = sv
			
def print_ais_options(f):
	f.write("aisexec {\n")
	for key in ais_options.keys():
		f.write("\t#%s\n\n" % (aisexec_option_table[key]["doc"]))
		f.write("\t%s:\t%s\n\n" % (key, ais_options[key]))
	f.write("}\n")	
	
def print_amf_options(f):
	f.write("amf {\n")
	for key in amf_options.keys ():
		f.write("\t#%s\n\n" % (amf_option_table[key]["doc"]))
		f.write("\t%s:\t%s\n\n" % (key, amf_options[key]))
	f.write("}\n")

def print_event_options(f):
	if event_options == {}: return
	f.write("event {\n")
	for key in event_options.keys ():
		f.write("\t#%s\n\n" % (event_option_table[key]["doc"]))
		f.write("\t%s:\t%s\n\n" % (key, event_options[key]))
	f.write("}\n")
		
def print_service_options(f):
	for key in service_options.keys():
		f.write("service {\n")
		for k1 in service_options[key].keys():
			f.write("\t%s:\t%s\n\n" % (k1, service_options[key][k1]))
		f.write("}\n")
		
def print_pacemaker_service_options(f):
	f.write("service {\n")
	for key in pacemaker_service_options.keys():
		if service_option_table.get(key, None) != None:
			f.write("\t#%s\n\n" % (service_option_table[key]["doc"]))
		f.write("\t%s:\t%s\n\n" % (key, pacemaker_service_options[key]))
	f.write("}\n")	
			
def print_logging_options(f):
	f.write("logging {\n")
	for key in logging_options.keys():
		if key == "logger":
			for log in logging_options["logger"]:
				f.write("\tlogger {\n")
				for l in log.keys():
					f.write("\t\t#%s\n\n" % (logger_option_table[l]["doc"]))
					f.write("\t\t%s:\t%s\n\n" % (l, log[l]))
				f.write("\t}\n")
			continue
		f.write("\t#%s\n\n" % (logging_option_table[key]["doc"]))
		f.write("\t%s:\t%s\n\n" % (key, logging_options[key]))
	f.write("}\n")

def print_totem_options(f):
	f.write("totem {\n")
	for key in totem_options.keys():
		if key == "interface":
			for inf in totem_options["interface"]:
				f.write("\tinterface {\n")
				for k in inf.keys():
					f.write("\t\t#%s\n\n" % (interface_option_table[k]["doc"]))
					f.write("\t\t%s:\t%s\n\n" % (k, inf[k]))
				f.write("\t}\n")
			continue
		f.write("\t#%s\n\n" % (totem_option_table[key]["doc"]))
		f.write("\t%s:\t%s\n\n" % (key, totem_options[key]))
	# We print out all possible configurations as well
	for opt in totem_option_table.keys():
		v = totem_options.get(opt, None)
		if v == None:
			f.write("\t#%s\n\n" % (totem_option_table[opt]["doc"]))
			f.write("\t#%s:\t%s\n\n" % (opt, totem_option_table[opt]["default_value"]))
	f.write("}\n")


def file_parser(file):
	global ais_options
	global totem_options
	global pacemaker_service_options
	global service_options
	global logging_options
	global amf_options
	global event_options
	
	for l in file:
		i = strip_comments_and_pending_space(l)
		if i == "":
			continue

		if i[-1] == "{":
			i = i.lstrip().split(" ")[0]
			if i == "aisexec":
				ais_options = opt_parser(file, aisexec_option_table)
			elif i == "service":
				o = opt_parser(file, service_option_table)
				if o.get("name", "") == "pacemaker":
					pacemaker_service_options = o
				elif o.get("name", "") != "":
					service_options[o["name"]] = o
				else:
					pass
			elif i == "totem":
				totem_options = opt_parser(file, totem_option_table)
			elif i == "logging":
				logging_options = opt_parser(file, logging_option_table)
			elif i == "amf":
				amf_options = opt_parser(file, amf_option_table)
			elif i == "event":
				event_options = opt_parser(file, event_option_table)
			else:
				pass
	
def opt_parser(file, options):
	result = {}
	i = ""
	while (i == ""):
		i = get_next_line(file)

	while (i[-1] != "}"):
		if (i[-1] == "{"):
			if i.lstrip().split(" ")[0] == "interface":
				infs = result.get("interface", [])
				infs.append(opt_parser(file, interface_option_table))
				result["interface"] = infs		
			elif i.lstrip().split(" ")[0] == "logger":
				logs = result.get("logger", [])
				logs.append(opt_parser(file, logger_option_table))
				result["logger"] = logs
			else:
				msgbox("Unknown sub-directive %s found. Ignore it" % (i.lstrip().split(" ")[0]))
				while (i[-1] != "}"):
					i = get_next_line(file)
				
			i = get_next_line(file)
			while ( i == ""):
				i = get_next_line(file)
			continue
		
		opt = i.split(":")
		try:
			doc = options[opt[0].strip()]["doc"]
		except KeyError:
			print "Unknown options", opt[0].strip()
			if options == service_option_table:
				result[opt[0].strip()] = opt[1].strip()
			else:
				msgbox("Unknown options %s found, ignore it" % (opt[0].strip()))
		else:
			if options[opt[0].strip()].get("type", "string") == "int":
				try:
					result[opt[0].strip()] = int(opt[1].strip())
				except ValueError:
					msgbox("Invalid option %s found, default to %s" % (opt[0].strip(), options[opt[0].strip()]["default_value"]))
					result[opt[0].strip()] = options[opt[0].strip()]["default_value"]
			else:
				result[opt[0].strip()] = opt[1].strip()
		i = ""
		while (i == ""):
			i = get_next_line(file)
	return result.copy()

def validate_conf():
	if totem_options.get("version", 0) != 2:
		return 1, "Version has to be set to 2"
	inf1 = get_interface0()
	inf2 = get_interface1()
	if inf1 == None and inf2 != None:
		return 1, "Ringnumber 1 is specified while ringnumber 0 is not"
	if len(totem_options.get("interface", [])) == 0:
		return 1, "No interface specified"
	if len(totem_options.get("interface", []))>2:
		return 1, "More then 2 interfaces specified"
	for inf in totem_options["interface"]:
		if inf.get("mcastaddr", "") == "":
			return 1, "No multicast address specified"
		if inf.get("mcastport", 0) == 0:
			return 1, "No multicast port specified"
		if inf.get("ringnumber", -1) != 0 and inf.get("ringnumber", -1) != 1:
			return 1, "Ring Number must be 0 or 1, but got %d" %(inf.get("ringnumber", -1))
		try:
			inf.get("mcastaddr", "").index(':')
			if totem_options.get("nodeid", 0) == 0:
				return 1, "Node ID must be specified for IPv6"
		except ValueError:
			pass
	return 0, "OK"

def get_interface0():
	for inf in totem_options.get("interface", []):
		if inf["ringnumber"] == 0:
			return inf
	else:
		return None

def get_interface1():
	for inf in totem_options.get("interface", []):
		if inf["ringnumber"] == 1:
			return inf
	else:
		return None
	
def init_options():
	generate_default_ais_options()
		
	fulfill_default_amf_options()
	fulfill_default_pacemaker_options()
	fulfill_default_logging_options()		
	fulfill_suggested_totem_options()
		
def load_ais_conf(filename):
	try:
		f = open(filename, "r")
		file_parser(f)
		f.close()
	except IOError:
		f = open(filename, "w")
		f.write(" ")
		f.close()
		load_ais_conf(filename)

		top_window = None

def msgbox(msg) :
	global top_window
	dialog = gtk.Dialog("Message", top_window, gtk.DIALOG_MODAL, (gtk.STOCK_OK, True))
	dialog.set_border_width(5)
	im=gtk.Image()
	im.set_from_stock(gtk.STOCK_DIALOG_INFO, gtk.ICON_SIZE_DIALOG)
	hb=gtk.HBox()
	hb.pack_start(im)
	label = gtk.Label(msg)
	label.set_selectable(True)
	label.set_line_wrap(True)
	hb.pack_start(label)
	dialog.vbox.pack_start(hb)
	dialog.show_all()
	save_top_window = top_window
	top_window = dialog
	dialog.run()
	top_window = save_top_window
	dialog.destroy()
	
class OpenAISConfDialog:
	def toggle_interface2(self, interface2):
		if self.wTree.get_widget("chk_interface2").get_active():
			self.wTree.get_widget("text_bindnetaddr2").set_sensitive(True)
			self.wTree.get_widget("text_mcastaddr2").set_sensitive(True)
			self.wTree.get_widget("text_mcastport2").set_sensitive(True)
		else:
			self.wTree.get_widget("text_bindnetaddr2").set_sensitive(False)
			self.wTree.get_widget("text_mcastaddr2").set_sensitive(False)
			self.wTree.get_widget("text_mcastport2").set_sensitive(False)			

	def toggle_secauth(self, secauth):
		if secauth.get_active():
			self.wTree.get_widget("text_threads").set_sensitive(True)
		else:
			self.wTree.get_widget("text_threads").set_sensitive(False)
			self.wTree.get_widget("text_threads").set_text(str(0))
			
	def toggle_clearbit(self, clearbit):
		if clearbit.get_active():
			self.wTree.get_widget("text_nodeid").set_sensitive(False)
		else:
			self.wTree.get_widget("text_nodeid").set_sensitive(True)
			
	def quit(self, menu):
		self.window.destroy()
		
	def __init__(self):
		self.gladefile="/usr/share/heartbeat-gui/pacemaker-starter.glade"
		self.wTree = gtk.glade.XML(self.gladefile)
		self.window = self.wTree.get_widget("top_window")

		if (self.window):
			self.window.connect("destroy", gtk.main_quit)

		self.wTree.get_widget("SaveMenu").connect("activate", self.save_configuration)
		self.wTree.get_widget("QuitMenu").connect("activate", self.quit)
		
		# Setup tooltips
		
		self.wTree.get_widget("text_bindnetaddr").set_tooltip_text(interface_option_table["bindnetaddr"]["doc"])
		self.wTree.get_widget("text_mcastaddr").set_tooltip_text(interface_option_table["mcastaddr"]["doc"])
		self.wTree.get_widget("text_mcastport").set_tooltip_text(interface_option_table["mcastport"]["doc"])
		self.wTree.get_widget("text_bindnetaddr2").set_tooltip_text(interface_option_table["bindnetaddr"]["doc"])
		self.wTree.get_widget("text_mcastaddr2").set_tooltip_text(interface_option_table["mcastaddr"]["doc"])
		self.wTree.get_widget("text_mcastport2").set_tooltip_text(interface_option_table["mcastport"]["doc"])

		self.wTree.get_widget("text_nodeid").set_tooltip_text(totem_option_table["nodeid"]["doc"])
		self.wTree.get_widget("text_threads").set_tooltip_text(totem_option_table["threads"]["doc"])
		self.wTree.get_widget("chk_clear_high_bit").set_tooltip_text(totem_option_table["clear_node_high_bit"]["doc"])
		self.wTree.get_widget("chk_secauth").set_tooltip_text(totem_option_table["secauth"]["doc"])
		
		self.wTree.get_widget("chk_mgmtd").set_tooltip_text(service_option_table["use_mgmtd"]["doc"])
		
		#Setup interface
		
		# if only 1 interface specified, it must be ringnumber 0
		if len(totem_options["interface"]) == 1:
			totem_options["interface"][0]["ringnumber"] = 0
			
		inf = get_interface0()
		if inf != None:
			self.wTree.get_widget("text_bindnetaddr").set_text(inf.get("bindnetaddr", ""))
			self.wTree.get_widget("text_mcastaddr").set_text(inf.get("mcastaddr", ""))
			self.wTree.get_widget("text_mcastport").set_text(str(inf.get("mcastport", "")))
		
		inf = get_interface1()
		self.wTree.get_widget("chk_interface2").connect("toggled", self.toggle_interface2)

		if inf != None:
			self.wTree.get_widget("text_bindnetaddr2").set_text(inf.get("bindnetaddr", ""))
			self.wTree.get_widget("text_mcastaddr2").set_text(inf.get("mcastaddr", ""))
			self.wTree.get_widget("text_mcastport2").set_text(str(inf.get("mcastport", "")))
			self.wTree.get_widget("chk_interface2").set_active(True)
		else:
			self.wTree.get_widget("chk_interface2").set_active(False)
			
		self.wTree.get_widget("chk_interface2").toggled()

		#setup totem
		if totem_options.get("nodeid", 0) != 0:
			self.wTree.get_widget("text_nodeid").set_text(str(totem_options.get("nodeid", 0)))
		else:
			self.wTree.get_widget("text_nodeid").set_sensitive(False)
			
		if self.wTree.get_widget("text_nodeid").get_text() != "":
			self.wTree.get_widget("chk_clear_high_bit").set_active(False)
		else:
			self.wTree.get_widget("chk_clear_high_bit").set_active(True)
		self.wTree.get_widget("chk_clear_high_bit").connect("toggled", self.toggle_clearbit)
			
			
		self.wTree.get_widget("text_threads").set_text(str(totem_options.get("threads", 0)))
			
		if int(self.wTree.get_widget("text_threads").get_text()) != 0:
			self.wTree.get_widget("chk_secauth").set_active(True)
		else:
			self.wTree.get_widget("chk_secauth").set_active(False)
			self.wTree.get_widget("text_threads").set_sensitive(False)
		self.wTree.get_widget("chk_secauth").connect("toggled", self.toggle_secauth)
		
		mg = pacemaker_service_options.get("use_mgmtd", "no")
		if is_ais_true(mg):
			self.wTree.get_widget("chk_mgmtd").set_active(True)
		else:
			self.wTree.get_widget("chk_mgmtd").set_active(False)
			
		
	def sync_setting(self):
		if get_interface0() == None:
			totem_options["interface"].append({"ringnumber":0})
			
		s = self.wTree.get_widget("text_bindnetaddr").get_text()
		if s != "":
			get_interface0()["bindnetaddr"] = s
		else:
			return 1, "Bind Network Address has to be assigned for the first interface"
		s = self.wTree.get_widget("text_mcastaddr").get_text()
		if s != "":
			get_interface0()["mcastaddr"] = s
		else:
			return 1, "Multicast address has to be assigned for the first interface"
		s = self.wTree.get_widget("text_mcastport").get_text()
		if s == "":
			return 1, "Multicast port has to be assigned for the first interface"
		try:
			i = int(s)
			get_interface0()["mcastport"] = i
		except ValueError:
			return 1, "Multicast Port has to be integer, but got a %s for the first interface" % (s)

		if self.wTree.get_widget("chk_interface2").get_active():
			if get_interface1() == None:
				# We dont have second interface yet. Create it first.
				totem_options["interface"].append({"ringnumber":1})
			s = self.wTree.get_widget("text_bindnetaddr2").get_text()
			if s != "":
				get_interface1()["bindnetaddr"] = s
			else:
				return 1, "Bind Network Address has to be assigned for the second interface"
			s = self.wTree.get_widget("text_mcastaddr2").get_text()
			if s != "":
				get_interface1()["mcastaddr"] = s
			else:
				return 1, "Multicast address has to be assigned for the second interface"
			s = self.wTree.get_widget("text_mcastport2").get_text()
			if s == "":
				return 1, "Multicast port has to be assigned for the second interface"
			try:
				i = int(s)
				get_interface1()["mcastport"] = i
			except ValueError:
				return 1, "Multicast Port has to be integer, but got a %s for the second interface" % (s)

		else:
			# Dont use the redudent interface anymore, delete it 
			for i in range(len(totem_options["interface"])):
				if totem_options["interface"][i]["ringnumber"] == 1:
					del totem_options["interface"][i]
					break
		
		if len(totem_options["interface"]) == 1:
			totem_options["rrp_mode"] = "none"
		elif len(totem_options["interface"]) == 2:
			rr = totem_options.get("rrp_mode", None)
			if rr == "active" or rr == "passive":
				pass
			else:
				totem_options["rrp_mode"] = "passive"
		else:
			if totem_options.get("rrp_mode", None) != None:
				del totem_options["rrp_mode"]
		
		if self.wTree.get_widget("chk_clear_high_bit").get_active():
			totem_options["clear_node_high_bit"] = "yes"
			try:
				del totem_options["nodeid"]
			except KeyError:
				pass
		else:
			s = self.wTree.get_widget("text_nodeid").get_text()
			try:
				i = int(s)
				totem_options["nodeid"] = i
			except ValueError:
				return 1, "Node ID must be assigned as a integer"
			try:
				del totem_options["clear_node_high_bit"]
			except KeyError:
				pass
			
		if not self.wTree.get_widget("chk_secauth").get_active():
			totem_options["secauth"] = "off"
			try:
				del totem_options["threads"]
			except KeyError:
				pass
		else:
			s = self.wTree.get_widget("text_threads").get_text()
			try:
				i = int(s)
				if i == 0:
					return 1, "Number of threads must bigger than 0"
				totem_options["threads"] = i
				totem_options["secauth"] = "on"
			except ValueError:
				return 1, "Number of threads must be integer"
			if totem_options.get("keyfile", None) == None:
				totem_options["keyfile"] = totem_option_table["keyfile"]["default_value"]
			
		if self.wTree.get_widget("chk_mgmtd").get_active():
			pacemaker_service_options["use_mgmtd"] = "yes"
		else:
			pacemaker_service_options["use_mgmtd"] = "no"
			
		return 0, "Everything's OK"
	
	def save_configuration(self, menu):
		generate_default_ais_options()
		
		fulfill_default_amf_options()
		fulfill_default_pacemaker_options()
		fulfill_default_logging_options()		
		fulfill_suggested_totem_options()

		r, reason = self.sync_setting()
		if r == 1:
			msgbox(reason)
			return 1
	
		r, reason = validate_conf()
		if r == 1:
			msgbox(reason)
			return 1
		
		f = open("/etc/ais/openais.conf.tmp", "w")
		print_ais_options(f)
		print_pacemaker_service_options(f)
		print_service_options(f)
		print_totem_options(f)
		print_logging_options(f)
		print_amf_options(f)
		print_event_options(f)
		f.close()
		if totem_options.get("secauth", "off") == "on":
			msgbox("Security Authrization is turned on.\nKeyfile is /etc/ais/authkey, with 128-bytes random characters.\nYou can either create it manually or copy over from other nodes in the cluster.")
		try:
			os.rename("/etc/ais/openais.conf", "/etc/ais/openais.conf.bak")
			os.rename("/etc/ais/openais.conf.tmp", "/etc/ais/openais.conf")
		except OSError:
			pass
		else:
			msgbox("New configuration saved")
		
if __name__ == "__main__":
	
	if os.getuid() != 0:
		msgbox("You must be ROOT to use this tool")
		sys.exit(1)
		
	load_ais_conf("/etc/ais/openais.conf")
	r, reason = validate_conf()
	if r == 1:
		msgbox(reason)
	hwg = OpenAISConfDialog()
	gtk.main()