#!@PYTHON@

'''haclient.py, the GUI manamgement tool for Linux-HA
'''

__copyright__='''
Author: Huang Zhen <zhenhltc@cn.ibm.com>
Copyright (C) 2005 International Business Machines
'''

#
# 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.


import sys, os, string, socket, syslog, webbrowser, pickle, xml, gc
from xml.dom.minidom import parseString

import locale, gettext
app_name = "haclient"

sys.path.append("@HA_DATADIR@/heartbeat-gui")
sys.path.append("@HA_LIBDIR@/heartbeat-gui")
from pymgmt import *

import pygtk
pygtk.require('2.0')
import gtk, gtk.glade, gobject

UI_FILE = "@HA_DATADIR@/heartbeat-gui/haclient.glade"

window = None
manager = None
debug_level = 0
top_window = None
last_rsc_view_page_num = 0	
last_compound_view_page_num = 0
last_cluster_view_page_num = 0

def log(string) :
	syslog.syslog(string)
	if debug_level > 0 :
		print string
		
def debug(string) :
	if debug_level == 0 :
		return
	syslog.syslog(string)
	print string
	
def uuid() :
	return os.popen("uuidgen").readline()[:-1]

def cond(condition, vtrue, vfalse) :
	if condition :
		return vtrue
	return vfalse
	
def edited_cb(cell, path, new_text, user_data):
      liststore, column, call_back = user_data
      liststore[path][column] = new_text
      if call_back != None :
	      call_back(None)
      return

def check_entry_value(glade, entry, label=None) :
	if label == None :
		label = entry
	if glade.get_widget(entry).get_text() == "":
		msgbox(label + _(" can't be empty"))
		return False
	return True
	
def pygtk_2_6_newer () :
	if gtk.pygtk_version[0] > 2 :
		return True
	if gtk.pygtk_version[0] == 2 and  gtk.pygtk_version[1] >= 6 :
		return True
	return False

def pygtk_2_4_newer () :
	if gtk.pygtk_version[0] > 2 :
		return True
	if gtk.pygtk_version[0] == 2 and  gtk.pygtk_version[1] >= 4 :
		return True
	return False
	
def add_column(widget, label, value, icon_cell_func = None, editable = False
		, options=None, call_back=None, visible = True) :
	tvcolumn = gtk.TreeViewColumn(label)
	widget.append_column(tvcolumn)
	if options == None or not pygtk_2_6_newer():
		cell = gtk.CellRendererText()
	else :
		cell = gtk.CellRendererCombo()
		store = gtk.ListStore(str)
		cell.set_property("model",store)
		for option in options :
			store.append([option])
		cell.set_property("text-column",0)
	
	if editable :
		cell.set_property('editable', True)
		cell.connect('edited', edited_cb, (widget.get_model(), value, call_back))
	if icon_cell_func != None :
		icon_cell = gtk.CellRendererPixbuf()
		tvcolumn.pack_start(icon_cell, False)
	        tvcolumn.set_cell_data_func(icon_cell, icon_cell_func)
	tvcolumn.set_resizable(True)        
	tvcolumn.pack_start(cell, True)
	tvcolumn.add_attribute(cell, 'text', value)
	tvcolumn.set_visible(visible)
	
def msgbox(msg) :
	global top_window
	dialog = gtk.Dialog(_("Message"), top_window, gtk.DIALOG_MODAL, (gtk.STOCK_OK, True))
	label = gtk.Label(msg)
	dialog.vbox.add(label)
	label.show()
	save_top_window = top_window
	top_window = dialog
	dialog.run()
	top_window = save_top_window
	dialog.destroy()


def confirmbox(msg, button=(gtk.STOCK_YES, gtk.RESPONSE_YES, gtk.STOCK_NO, gtk.RESPONSE_NO), title ="") :
	global top_window
	dialog = gtk.Dialog(_("Confirm"),  top_window, gtk.DIALOG_MODAL,button)
	im=gtk.Image()
	im.set_from_stock(gtk.STOCK_DIALOG_QUESTION, gtk.ICON_SIZE_DIALOG)
	frame = gtk.Frame()
	confirm_title = gtk.Label()
	confirm_title.set_markup('<b>'+title+'</b>')
	frame.set_label_widget(confirm_title)
	confirm_msg =  gtk.Label()
	confirm_msg.set_text(msg)
	dialog.vbox.pack_start(frame)
	hb=gtk.HBox()
	hb.pack_start(im)
	hb.pack_start(confirm_msg)
	frame.add(hb)
	dialog.vbox.resize_children()
	dialog.set_default_response(gtk.RESPONSE_YES)
	dialog.show_all()
	save_top_window = top_window
	top_window = dialog
	ret = dialog.run()
	top_window = save_top_window
	dialog.destroy()
	return ret 
	
def on_label_active(event, widget, url) :
	if not url[0] == '/' and not url.startswith("http") :
		url = "http://"+url
	webbrowser.open(url)

def make_label_active(label, text, url) :
	label.set_markup('<span foreground="blue"><u>'+text+'</u></span>')
	label.get_parent().connect("button_press_event", on_label_active, url)
	#label.get_parent().window.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND1))

def kvbox(title, fields, auto_fill = None) :
	global top_window
	dialog = gtk.Dialog(title, top_window, gtk.DIALOG_MODAL,
		(gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
	table = gtk.Table(2, 1, False)
	widgets = []
	combos = {}
	for row in range(len(fields)):
		if fields[row].auto_gen :
			widgets.append(None)
			continue
		table.attach(gtk.Label(fields[row].label+":"), 0, 1, row, row+1)
		if fields[row].options != None and fields[row].options!=[]:
			combo = gtk.combo_box_entry_new_text()
			for option in fields[row].options:
				combo.append_text(option)
			if fields[row].default != None :
				combo.child.set_text(fields[row].default)
			else :
				combo.child.set_text(fields[row].options[0])
			widgets.append(combo)
			table.attach(combo, 1,2, row, row+1)
			combos[fields[row].key] = combo
		else :
			entry = gtk.Entry()
			if fields[row].default != None :
				entry.set_text(fields[row].default)
			widgets.append(entry)
			table.attach(entry, 1,2, row, row+1)

	if auto_fill != None :
		if title == _("Add Parameter") and combos.has_key("name"):
			combos["name"].connect("changed", auto_fill, widgets)
		elif title == _("Add Operation") :
			if combos.has_key("name"):
				combos["name"].connect("changed", auto_fill, widgets, "name")
			if combos.has_key("role"):
				combos["role"].connect("changed", auto_fill, widgets, "role")
	
	dialog.vbox.pack_start(table)
	dialog.vbox.show_all()
	save_top_window = top_window
	top_window = dialog

	while True :
		ret = dialog.run()
		if ret in [gtk.RESPONSE_CANCEL,gtk.RESPONSE_DELETE_EVENT] :
			top_window = save_top_window
			dialog.destroy()
			return None
		else :
			kv = {}
			passed = True
			for row in range(len(fields)):
				if fields[row].auto_gen :
					kv[fields[row].key] = uuid()
					continue
				if fields[row].options != None and fields[row].options != []:
					combo = widgets[row]
					kv[fields[row].key] = combo.child.get_text()
				else :
					entry = widgets[row]
					kv[fields[row].key] = entry.get_text()
				if not fields[row].can_be_empty and kv[fields[row].key] == "" :
					msgbox(fields[row].label+_(" can't be empty"))
					passed = False
			if passed :
				top_window = save_top_window
				dialog.destroy()
				return kv

class Field :
	key = None
	label = None
	default = None
	options = None
	can_be_empty = True
	editable = True
	auto_gen = False
	def __init__(self, key, label=None, default=None, options=None,
			can_be_empty=True, editable=True, auto_gen=False) :
		self.key = key
		self.label = cond(label==None, key, label)
		self.default = default
		self.options = options
		self.can_be_empty = can_be_empty
		self.editable = editable
		self.auto_gen = auto_gen

class RAMeta :
	name = ""
	version = None
	desc = ""
	parameters = []
	actions = []

def NVBox(option_dict) :
	def on_name_change(name_combo, value_combo, option_dict) :
		name = name_combo.child.get_text()
		value_combo.get_model().clear()
		value_combo.child.set_text("")
		if name in option_dict.keys() :
			for option in option_dict[name] :
				value_combo.append_text(option)
			if option_dict[name] != [] :
				value_combo.child.set_text(option_dict[name][0])
		
	global top_window
	dialog = gtk.Dialog(_("Add Name/Value Pair"), top_window, gtk.DIALOG_MODAL,
		(gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
	table = gtk.Table(2, 1, False)
	
	table.attach(gtk.Label(_("Value")+":"), 0, 1, 1, 2)
	value_combo = gtk.combo_box_entry_new_text()
	table.attach(value_combo, 1, 2, 1, 2)
	
	table.attach(gtk.Label(_("Name")+":"), 0, 1, 0, 1)
	name_combo = gtk.combo_box_entry_new_text()
	for name in option_dict.keys() :
		name_combo.append_text(name)
	table.attach(name_combo, 1, 2, 0, 1)
	name_combo.connect("changed", on_name_change, value_combo, option_dict)
	name_combo.child.set_text(option_dict.keys()[0])
	
	dialog.vbox.pack_start(table)
	dialog.vbox.show_all()
	save_top_window = top_window
	top_window = dialog

	while True :
		ret = dialog.run()
		if ret in [gtk.RESPONSE_CANCEL,gtk.RESPONSE_DELETE_EVENT] :
			top_window = save_top_window
			dialog.destroy()
			return (None,None)
		else :
			name = name_combo.child.get_text()
			value = value_combo.child.get_text()
			top_window = save_top_window
			dialog.destroy()
			return (name, value)

class NVList :
	widget = None
	store = None
	value_cell = None
	call_back = None
	options = {}
	option_dict = {}
	empty_model = gtk.ListStore(str)
	def on_cursor_changed(self, treeview, selection) :
		(model, iter) = selection.get_selected()
		name = model.get_value(iter, 0)
		if self.options != None :
			if name in self.options.keys() :
				self.value_cell.set_property("model", self.options[name])
			else :
				self.value_cell.set_property("model", self.empty_model)
	
	def on_del(self, widget) :
		selection = self.widget.get_selection()
		(model, iter) = selection.get_selected()
		if iter != None :
			model.remove(iter)
			if self.call_back != None :
				self.call_back(self)
	
		
	def on_add(self, widget) :
		(name,value) = NVBox(self.option_dict)
		if name != None :
			self.insert(name, value) 
			self.call_back(self)
	
	def __init__(self, widget, add_btn, del_btn, option_dict=None, call_back=None) :
		self.widget = widget
		self.store = gtk.ListStore(str, str)
		widget.set_model(self.store)
		
		cell = gtk.CellRendererText()
		tvcolumn = gtk.TreeViewColumn(_("Name"),cell,text=0)
		widget.append_column(tvcolumn)
		
		if pygtk_2_6_newer():
			cell = gtk.CellRendererCombo()
			model = gtk.ListStore(str)
			cell.set_property("model",model);
			cell.set_property("text-column", 0);
			self.value_cell = cell
			widget.connect("cursor-changed", self.on_cursor_changed, 
						widget.get_selection())
		else :
			cell = gtk.CellRendererText()
		cell.set_property('editable', True)
		cell.connect('edited', edited_cb, (self.store, 1, call_back))
		tvcolumn = gtk.TreeViewColumn(_("Value"),cell,text=1)
		self.call_back = call_back
		self.widget.append_column(tvcolumn)
		add_btn.connect("clicked", self.on_add)
		del_btn.connect("clicked", self.on_del)
		self.option_dict = option_dict
		if option_dict != None :
			for name in option_dict.keys() :
				option_model = gtk.ListStore(str)
				for option in option_dict[name] :
					option_model.append([option])
				self.options[name]=option_model
		
	def insert(self, name, value) :		
		self.store.append([name, value])
		
	def clear(self) :
		self.store.clear()
		
	def get_data(self) :	
		nv = {}
		iter = self.store.get_iter_first()
		while iter != None :
			name = self.store.get_value(iter,0)
			value = self.store.get_value(iter,1)
			nv[name]=value
			iter = self.store.iter_next(iter)
		return nv	
		
class ListWithAddDel :
	add_btn = None
	del_btn = None
	widget = None
	call_back = None
	store = None
	fields = None
	add_btn_handler = None
	del_btn_handler = None
	def on_del(self, widget) :
		selection = self.widget.get_selection()
		(model, iter) = selection.get_selected()
		if iter != None :
			model.remove(iter)
			if self.call_back != None :
				self.call_back(self)
	
		
	def on_add(self, widget) :
		new = kvbox(self.title, filter(lambda f:f.editable, self.fields), self.auto_fill)
		if new != None :
			self.insert(new)
			
	def create_store(self, n) :
		if n == 3 :
			return gtk.ListStore(str, str, str)
		if n == 4 :
			return gtk.ListStore(str, str, str, str)
		if n == 10 :
			return gtk.ListStore(str, str, str, str, str,str, str, str, str, str)
		return None
		
	def __init__(self, widget, add_btn, del_btn, title, fields, call_back=None, auto_fill=None) :
		self.add_btn = add_btn
		self.del_btn = del_btn
		self.widget = widget
		self.call_back = call_back
		self.title = title
 		self.fields = fields
		self.add_btn_handler = add_btn.connect("clicked", self.on_add)
		self.del_btn_handler = del_btn.connect("clicked", self.on_del)
		self.auto_fill = auto_fill
		self.store = self.create_store(len(fields))
		widget.set_model(self.store)
		for i in range(len(fields)) :
			add_column(widget, fields[i].label, i, None, fields[i].editable,
				fields[i].options, call_back, not fields[i].auto_gen)
			
		
	def clear(self) :
		self.store.clear()
		
	def destory(self) :
		self.clear()
		for c in self.widget.get_columns() :
			self.widget.remove_column(c)
		self.add_btn.disconnect(self.add_btn_handler)
 		self.del_btn.disconnect(self.del_btn_handler)
			
	def insert(self, item) :
		values = []
		for f in self.fields :
			if item.has_key(f.key) :
				values.append(item[f.key])
			else :
				values.append("")
		self.store.append(values)
		if self.call_back != None :
			self.call_back(self)
		
	def get_data(self) :
		iter = self.store.get_iter_first()
		items = []
		while iter != None :
			item = {}
			for i in range(len(self.fields)) :
				item[self.fields[i].key] = self.store.get_value(iter,i)
			items.append(item)
			iter = self.store.iter_next(iter)
		return items
	
			
class Tree :
	store = None
	widget = None
	last_iter = None
	def on_right_click(self, widget, event) :
		if event.type == gtk.gdk.BUTTON_PRESS and event.button == 3 :
			menu = None
			if window.cur_type in ["node"] :
				menu = window.uimanager.get_widget("/node_popup")
			elif window.cur_type in ["cluster"] or window.cur_type == None:
				menu = window.uimanager.get_widget("/connection_popup")
			else :
				menu = window.uimanager.get_widget("/resource_popup")
			menu.popup(None, None, None, event.button, event.time)

	def __init__(self, widget) :
		self.store = gtk.TreeStore(str, str, str)
		widget.set_model(self.store)
		add_column(widget, _("Name"), 0, self.render_icon)
		add_column(widget, _("Status"), 1)
		widget.set_size_request(300, 100)
		widget.connect("cursor-changed", self.on_cursor_changed, widget.get_selection())
		widget.connect("event-after", self.on_right_click)
		self.widget = widget
	
	def on_cursor_changed(self, treeview, selection) :
		(model, iter) = selection.get_selected()
		if not window.can_change_view() :
			if self.last_iter != None :
				selection.select_iter(self.last_iter)
				return
		if iter == None :
			window.select_view(None, None, None)
			return
		type = model.get_value(iter, 2)
		name = model.get_value(iter, 0)
		status = model.get_value(iter, 1)
		window.select_view(type, name, status)
		self.last_iter = iter
		
	def select_row(self, model, path, iter, user_data):
		name, type = user_data
		selection = self.widget.get_selection()
		if (name, type) == (model.get_value(iter,0), model.get_value(iter,2)) :
			selection.select_path(path)
			return True
		
	def render_icon(self, tvcolumn, cell, model, iter):
 		icons = {-2:gtk.STOCK_DIALOG_INFO,
			 -1:gtk.STOCK_PREFERENCES,
			  0:gtk.STOCK_YES,
			  1:gtk.STOCK_DIALOG_INFO,
			  2:gtk.STOCK_DIALOG_WARNING,
			  3:gtk.STOCK_DIALOG_ERROR}
		status = self.iter_status(model,iter)
		pb = self.widget.render_icon(icons[status],
			gtk.ICON_SIZE_BUTTON, None)
       		cell.set_property('pixbuf', pb)
		return
	
	def iter_status(self, model, iter) :
		# -1:config 0: ok, 1: stopped, 2: umanaged, 3:failed, 4:stop failed
		type = model.get_value(iter, 2)
		status = model.get_value(iter, 1)
		iter_status = 0
		if type == _("native") :
			if string.find(status, _("not running")) != -1 :
				iter_status = 1
			elif string.find(status, _("running")) != -1 :
				iter_status = 0
			elif string.find(status, _("fail")) != -1 :
				iter_status = 3
			else :
				iter_status = 2
		
		elif type == "node" :
			if status == _("ping node") :
				iter_status = -1				
			elif string.find(status, _("running")) != -1 :
				iter_status = 0
			else :
				iter_status = 1
		elif type in [_("group"),_("resources"),_("clone"),_("master")]:
			iter_status = 0
		elif type in [_("Orders"),_("Locations"), _("Colocations")] :
			iter_status = -1
		else :
			iter_status = -2
		if model.iter_has_child(iter) :
			for i in range(0, model.iter_n_children(iter)) :
				child = model.iter_nth_child(iter,i)
				child_status = self.iter_status(model, child)
				if child_status > iter_status :
					iter_status = child_status
		
		return iter_status				

	def save_iter_status(self, model, path, iter, expand_status):
		name = model.get_value(iter, 0)
		type = model.get_value(iter, 2)
		expand_status[(name,type)] = self.widget.row_expanded(path)

	
	def restore_iter_status(self, model, path, iter, user_data):
		(select_name, select_type, select_path, expand_status) = user_data
		name = model.get_value(iter, 0)
		type = model.get_value(iter, 2)
		path = model.get_path(iter)
		if name == select_name and type == select_type and path == select_path:
			self.widget.get_selection().select_iter(iter)
		if expand_status.has_key((name,type)) :
			if expand_status[name, type] :
				self.widget.expand_row(path, False)
			else :
				self.widget.collapse_row(path)
		else :
			self.widget.expand_row(path, False)
			
	def save_tree_status(self) :
		(model, iter) = self.widget.get_selection().get_selected()
		if iter != None :
			select_name = model.get_value(iter, 0)
			select_type = model.get_value(iter, 2)
			select_path = model.get_path(iter)
		else :
			select_name = None
			select_type = None
			select_path = None
		expand_status = {}
		self.store.foreach(self.save_iter_status, expand_status)
		return (select_name, select_type, select_path, expand_status)

	def restore_tree_status(self, tree_status) :
		self.store.foreach(self.restore_iter_status, tree_status)
		
	def update(self) :
		tree_status = self.save_tree_status()		
		
		self.store.clear()
		window.select_view(None, None, None)
		if not manager.connected :
			return
				
		
		nodes = manager.get_all_nodes()
		if nodes == None :
			return

		active_nodes = manager.get_active_nodes()
		if active_nodes == None :
			return

		crm_nodes = manager.get_crm_nodes()
		if crm_nodes == None :
			return
		
		config = manager.get_cluster_config()
		if config.has_key("have_quorum") and config["have_quorum"] == "true" :
			status = _("with quorum")
		else :
			status = _("without quorum")
		root = self.store.append(None, [config["cluster"],status, "cluster"])
		
		nodes_root = self.store.append(root, [_("Nodes"),"", "nodes"])
		for node in nodes :
			self.add_node(nodes_root, node, node in active_nodes, node in crm_nodes)

		rscs_root = self.store.append(root, [_("Resources"),"", "resources"])
		rscs = manager.get_all_rsc_id()
		for rsc in rscs :
			self.add_rsc(rscs_root, rsc)

		constraints_root = self.store.append(root, [_("Constraints"),"", "constraints"])
		
		locations_root = self.store.append(constraints_root, [_("Locations"),"", "locations"])
		locations = manager.get_constraints("rsc_location")
		for location in locations :
			self.store.append(locations_root,[location["id"], "", _("location")])
		
		orders_root = self.store.append(constraints_root, [_("Orders"),"", "orders"])
		orders = manager.get_constraints("rsc_order")
		for order in orders :
			self.store.append(orders_root,[order["id"], "", _("order")])
			
		colocations_root = self.store.append(constraints_root, [_("Colocations"),"", "colocations"])
		colocations = manager.get_constraints("rsc_colocation")
		for colocation in colocations :
			self.store.append(colocations_root,[colocation["id"], "", _("colocation")])
		
		self.restore_tree_status(tree_status)
		(name, type, path, expand_status) = tree_status
		if  name == None :
			self.widget.get_selection().select_iter(root)
			self.widget.expand_all()
 		self.on_cursor_changed(self.widget, self.widget.get_selection())
		
		
	def add_rsc(self, parent, rsc) :
		type = manager.get_rsc_type(rsc)
		status = ""
		label = ""
		if type == "native" :
			label = _("native")
			status = manager.get_rsc_status(rsc)
			nodes = manager.get_rsc_running_on(rsc)
			if nodes != None and len(nodes)>0:
				self.store.append(parent,[rsc, _(status) + _(" on ")+str(nodes), label])
			else :
				self.store.append(parent,[rsc, _(status), label])
				
		elif type in ["group","clone","master"] :
			label = type
			status = type
			iter = self.store.append(parent,[rsc, _(status), _(label)])
			for subrsc in manager.get_rsc_sub_rsc(rsc) :
				self.add_rsc(iter, subrsc)
					
	def add_node(self, nodes_root, node, active, started):
		status = _("unknown")
		if not started :
			node_type = manager.get_nodetype(node)[0];
			if  node_type == "normal" :
				status = _("never started")
			elif node_type == "ping" :
				status = _("ping node")
			else :
				status = _("unknown type")
		else :
			if not active :
				status = _("stopped")
			else :		
				dc = manager.get_dc()
				if dc != None and node in dc :
					status = _("running(dc)")
				else :
					status = _("running")
			if manager.get_node_config(node)["standby"] == "True" :
				status = status + "-"+ _("standby")

		node_iter = self.store.append(nodes_root,[node, status, "node"])

		if active :
	 		running_rsc = manager.get_running_rsc(node)
 			for rsc in running_rsc :
 				self.add_rsc(node_iter, rsc)

class View :
	glade = None
	widget = None
	name = "emptyview"
	changed = False
	
	def __init__(self, param=None) :
		self.param = param
		self.glade = gtk.glade.XML(UI_FILE, self.name, "haclient")
		self.widget = self.glade.get_widget(self.name)
		if self.glade.get_widget("apply") != None :
			self.glade.get_widget("apply").connect("clicked", self.on_apply)
		if self.glade.get_widget("reset") != None :
			self.glade.get_widget("reset").connect("clicked", self.on_reset)
		if self.glade.get_widget("default") != None :
			self.glade.get_widget("default").connect("clicked", self.on_default)
			
	def update(self) :
		if self.glade.get_widget("apply") != None :
			self.glade.get_widget("apply").set_property("sensitive", False)
		if self.glade.get_widget("reset") != None :
			self.glade.get_widget("reset").set_property("sensitive", False)
		self.changed = False
	
	def on_changed(self, widget) :
		self.glade.get_widget("apply").set_property("sensitive", True)
		self.glade.get_widget("reset").set_property("sensitive", True)
		self.changed = True
	
	def on_reset(self, widget):
		self.update()
		
	def on_apply(self, widget):
		self.glade.get_widget("apply").set_property("sensitive", False)
		self.glade.get_widget("reset").set_property("sensitive", False)
		self.changed = False

	def on_default(self, widget):
		self.on_changed(widget)
		
	def on_after_show(self) :
		pass	
		
class NodeView(View) :
	name = "nodeview"
	def __init__(self, param=None) :
		View.__init__(self, param)
		self.update()

	def update(self) :
		glade = self.glade
		node = self.param
		config = manager.get_node_config(node)
		if config != {} :
			glade.get_widget("standby").set_text(config["standby"])
			glade.get_widget("uname").set_text(config["uname"])
			glade.get_widget("expected_up").set_text(config["expected_up"])
			glade.get_widget("is_dc").set_text(config["is_dc"])
			glade.get_widget("shutdown").set_text(config["shutdown"])
			glade.get_widget("online").set_text(config["online"])
			glade.get_widget("type").set_text(config["type"])
			glade.get_widget("unclean").set_text(config["unclean"])
	def on_after_show(self) :
		make_label_active(self.glade.get_widget("l_standby"),_("Standby:"),"www.linux-ha.org")
		make_label_active(self.glade.get_widget("l_uname"),_("Node Name:"),"www.linux-ha.org")
		make_label_active(self.glade.get_widget("l_is_dc"),_("Is it DC:"),"www.linux-ha.org")
		make_label_active(self.glade.get_widget("l_shutdown"),_("Shutdown:"),"www.linux-ha.org")
		make_label_active(self.glade.get_widget("l_online"),_("Online:"),"www.linux-ha.org")
		make_label_active(self.glade.get_widget("l_type"),_("Type:"),"www.linux-ha.org")
		make_label_active(self.glade.get_widget("l_unclean"),_("Unclean:"),"www.linux-ha.org")
		make_label_active(self.glade.get_widget("l_expected_up"),_("Expected up:"),"www.linux-ha.org")
	
		
class ClusterView(View) :
	name = "clusterview"
	pengine_metadata = None
	crmd_metadata = None
	pengine_widgets = None
	crmd_widgets = None
	
	def on_apply(self, widget):
		glade = self.glade
		
		current_page = glade.get_widget("notebook").get_current_page()
		if current_page == 1:
			(widgets, metadata) = (self.pengine_widgets, self.pengine_metadata)
		elif current_page == 2:
			(widgets, metadata) = (self.crmd_widgets, self.crmd_metadata)

		new_crm_config = {}
		for key in widgets["combos"].keys():
			new_crm_config[key] = widgets["combos"][key].child.get_text()
		for key in widgets["checkbuttons"].keys():
			if widgets["checkbuttons"][key].get_active():
				new_crm_config[key] = "true"
			else:
				new_crm_config[key] = "false"
		for key in widgets["entries"].keys():
			new_crm_config[key] = widgets["entries"][key].get_text()
		manager.update_crm_config(metadata, new_crm_config)

		View.on_apply(self, widget)

	def on_default(self, widget):
		current_page = self.glade.get_widget("notebook").get_current_page()
		if current_page == 1:
			(widgets, metadata) = (self.pengine_widgets, self.pengine_metadata)
		elif current_page == 2:
			(widgets, metadata) = (self.crmd_widgets, self.crmd_metadata)

		default_crm_config = {}
		for parameter in metadata.parameters:
			default_crm_config[parameter["name"]] = str(parameter["content"]["default"])

		if default_crm_config != {}:
			for key in widgets["combos"].keys():
				widgets["combos"][key].child.set_text(default_crm_config[key])
				widgets["combos"][key].child.set_editable(False)
			for key in widgets["checkbuttons"].keys():
				widgets["checkbuttons"][key].set_active("true" == default_crm_config[key])
			for key in widgets["entries"].keys():
				widgets["entries"][key].set_text(default_crm_config[key])

		View.on_default(self, widget)
	
	def update(self) :
		glade = self.glade
		config = manager.get_cluster_config()

		if config != None :
			glade.get_widget("hbversion").set_text(config["hbversion"])
			glade.get_widget("debug").set_text(str(config["debug"]))
			glade.get_widget("udpport").set_text(config["udpport"])
			glade.get_widget("keepalive").set_text(config["keepalive"])
			glade.get_widget("warntime").set_text(config["warntime"])
			glade.get_widget("deadtime").set_text(config["deadtime"])
			glade.get_widget("initdead").set_text(config["initdead"])

		for (widgets, metadata) in ((self.pengine_widgets, self.pengine_metadata),
						(self.crmd_widgets, self.crmd_metadata)):
			if metadata != None and widgets != None:
				crm_config = manager.get_crm_config(metadata)
				if crm_config != {}:
					for key in widgets["combos"].keys():
						widgets["combos"][key].child.set_text(crm_config[key])
						widgets["combos"][key].child.set_editable(False)
					for key in widgets["checkbuttons"].keys():
						widgets["checkbuttons"][key].set_active("true" == crm_config[key])
					for key in widgets["entries"].keys():
						widgets["entries"][key].set_text(crm_config[key])

		View.update(self)
	
	def on_after_show(self) :
		make_label_active(self.glade.get_widget("l_hbversion"),_("Version:"),"www.linux-ha.org")
		make_label_active(self.glade.get_widget("l_debug"),_("Debug Level:"),"www.linux-ha.org")
		make_label_active(self.glade.get_widget("l_udpport"),_("UDP Port:"),"www.linux-ha.org")
		make_label_active(self.glade.get_widget("l_keepalive"),_("Keep Alive:"),"www.linux-ha.org")
		make_label_active(self.glade.get_widget("l_warntime"),_("Warning Alive:"),"www.linux-ha.org")
		make_label_active(self.glade.get_widget("l_deadtime"),_("Dead Time:"),"www.linux-ha.org")
		make_label_active(self.glade.get_widget("l_initdead"),_("Initial Dead Time:"),"www.linux-ha.org")

		name_cap = lambda name: name.replace("-"," ").replace("_"," ").title().replace("Crmd ","CRMD ").\
						replace("Pe ", "PE ").replace("Dc ","DC ")

		for widgets in (self.pengine_widgets, self.crmd_widgets):
			if widgets != None:
				for key in widgets["labels"].keys():
					make_label_active(widgets["labels"][key], 
								_(name_cap(key)+":"),
								"www.linux-ha.org")
							
	def on_notebook_event(self, widget, page, page_num) :
		global last_cluster_view_page_num

		if self.changed == True:
			ret = confirmbox(_("There are unsaved changes in current page.")+"\n"+_("Apply the changes?"),
					(gtk.STOCK_YES, gtk.RESPONSE_YES, 
					gtk.STOCK_NO, gtk.RESPONSE_NO))
			if ret == gtk.RESPONSE_YES :
				self.on_apply(widget)
			if ret == gtk.RESPONSE_NO :
				self.update()

		if page_num == 0:
			self.glade.get_widget("default").set_property("sensitive", False)
		else:
			self.glade.get_widget("default").set_property("sensitive", True)

		last_cluster_view_page_num = page_num

	def __init__(self) :
		View.__init__(self)
		glade = self.glade

		self.pengine_metadata = manager.get_crm_metadata("pengine")
		self.crmd_metadata = manager.get_crm_metadata("crmd")

		if self.pengine_metadata != None:
			self.pengine_widgets = self.render_cluster_conf(self.pengine_metadata)
		if self.crmd_metadata != None:
			self.crmd_widgets = self.render_cluster_conf(self.crmd_metadata)

		notebook = glade.get_widget("notebook")
		notebook.connect("switch-page", self.on_notebook_event)
		notebook.set_current_page(last_cluster_view_page_num)

		if last_cluster_view_page_num == 0 :
			glade.get_widget("default").set_property("sensitive", False)

		self.update()



	def render_cluster_conf(self, metadata):
		if metadata.name == "Policy Engine":
			vbox = self.glade.get_widget("vbox_pengine")
		elif metadata.name == "CRM Daemon":
			vbox = self.glade.get_widget("vbox_crmd")

		widgets = {}
		labels = {}
		combos = {}
		checkbuttons = {}
		entries = {}
		table = gtk.Table(2, 1, False)
		parameters = metadata.parameters

		for row in range(len(parameters)):
			label = gtk.Label(parameters[row]["name"]+":")
			label.set_alignment(1, 0.5)
			eventbox = gtk.EventBox()
			eventbox.add(label)
			table.attach(eventbox, 0, 1, row, row+1, xpadding = 5)
			labels[parameters[row]["name"]] = label

			if parameters[row]["content"].has_key("values"):
				combo = gtk.combo_box_entry_new_text()
				for option in parameters[row]["content"]["values"]:
					combo.append_text(option)
				combo.connect("changed", self.on_changed)
				table.attach(combo, 1, 2, row, row+1, xpadding = 5)
				combos[parameters[row]["name"]] = combo
			elif parameters[row]["content"]["type"] == "boolean":
				checkbutton = gtk.CheckButton()
				checkbutton.connect("toggled", self.on_changed)
				table.attach(checkbutton, 1, 2, row, row+1, xpadding = 2)
				checkbuttons[parameters[row]["name"]] = checkbutton
			else:
				entry = gtk.Entry()
				entry.connect("changed", self.on_changed)
				table.attach(entry, 1,2, row, row+1, xpadding = 5)
				entries[parameters[row]["name"]] = entry 
	
		vbox.pack_start(table)
		vbox.show_all()

		widgets["labels"] = labels
		widgets["combos"] = combos
		widgets["checkbuttons"] = checkbuttons
		widgets["entries"] = entries
	
		return widgets

class OrderView(View) :
	name = "orderview"
	save_order = None
	def on_apply(self, widget):
		glade = self.glade
		order = self.save_order
		order["from"] = glade.get_widget("from").child.get_text()
		order["type"] = glade.get_widget("type").child.get_text()
		order["to"] = glade.get_widget("to").child.get_text()
		manager.update_constraint("rsc_order", order)
		
		View.on_apply(self, widget)
		
	
	def update(self) :
		glade = self.glade
		order = manager.get_constraint("rsc_order", self.param)
		if order != None :
 			glade.get_widget("id").set_text(order["id"])
 			glade.get_widget("from").child.set_text(order["from"])
 			glade.get_widget("from").child.set_editable(False)
 			glade.get_widget("to").child.set_text(order["to"])
 			glade.get_widget("to").child.set_editable(False)
 			glade.get_widget("type").child.set_text(order["type"])
 			glade.get_widget("type").child.set_editable(False)
			store = gtk.ListStore(str)
			for rsc in manager.get_all_rsc_id():
				store.append([rsc])
			glade.get_widget("to").set_model(store)
			glade.get_widget("from").set_model(store)
			self.save_order = order
		View.update(self)

	def __init__(self, order_id) :
		View.__init__(self,order_id)
		glade = self.glade
 		glade.get_widget("from").connect("changed", self.on_changed)
 		glade.get_widget("type").connect("changed", self.on_changed)
 		glade.get_widget("to").connect("changed", self.on_changed)
		self.update()
		
class ColocationView(View) :
	name = "colocationview"
	save_colocation = None
	def on_apply(self, widget):
		glade = self.glade
		colocation = self.save_colocation
		colocation["from"] = glade.get_widget("from").child.get_text()
		colocation["score"] = glade.get_widget("score").child.get_text()
		colocation["to"] = glade.get_widget("to").child.get_text()
		manager.update_constraint("rsc_colocation", colocation)
		
		View.on_apply(self, widget)
		
	
	def update(self) :
		glade = self.glade
		colocation = manager.get_constraint("rsc_colocation", self.param)
		if colocation != None :
			glade.get_widget("id").set_text(colocation["id"])
			glade.get_widget("from").child.set_text(colocation["from"])
			glade.get_widget("from").child.set_editable(False)
			glade.get_widget("to").child.set_text(colocation["to"])
			glade.get_widget("to").child.set_editable(False)
			glade.get_widget("score").child.set_text(colocation["score"])
			glade.get_widget("score").child.set_editable(False)
			store = gtk.ListStore(str)
			for rsc in manager.get_all_rsc_id():
				store.append([rsc])
			glade.get_widget("from").set_model(store)
			glade.get_widget("to").set_model(store)
			self.save_colocation = colocation
		View.update(self)

	def __init__(self, colo_id) :
		View.__init__(self, colo_id)
		glade = self.glade
		glade.get_widget("from").connect("changed", self.on_changed)
		glade.get_widget("to").connect("changed", self.on_changed)
		glade.get_widget("score").connect("changed", self.on_changed)
		self.update()		
				
class LocationView(View) :
	name = "locationview"
	expr_list = None
	save_location = None
	
	def on_apply(self, widget):
		glade = self.glade
		location = self.save_location
		location["rsc"] = glade.get_widget("resource").child.get_text()
		location["score"] = glade.get_widget("score").child.get_text()
		location["boolean_op"] = glade.get_widget("boolean_op").child.get_text()
		location["exprs"] = self.expr_list.get_data()
		manager.update_constraint("rsc_location",location)
		View.on_apply(self, widget)
							   
	def update(self) :
		glade = self.glade
		self.expr_list.clear()
		location = manager.get_constraint("rsc_location", self.param)
		if location != None :
			glade.get_widget("id").set_text(location["id"])
			glade.get_widget("resource").child.set_text(location["rsc"])
			glade.get_widget("resource").child.set_editable(False)
			glade.get_widget("score").child.set_text(location["score"])
			glade.get_widget("boolean_op").child.set_text(location["boolean_op"])
			glade.get_widget("boolean_op").child.set_editable(False)
			for expr in location["exprs"] :
				self.expr_list.insert(expr)
			store = gtk.ListStore(str)
			for rsc in manager.get_all_rsc_id():
				store.append([rsc])
			glade.get_widget("resource").set_model(store)
			self.save_location = location
		View.update(self)
		
	def __init__(self, rsc) :
		View.__init__(self, rsc)
		glade = self.glade
		glade.get_widget("resource").connect("changed", self.on_changed)
		glade.get_widget("score").connect("changed", self.on_changed)
		glade.get_widget("boolean_op").connect("changed", self.on_changed)
		self.expr_list = ListWithAddDel(glade.get_widget("expr_list"),
			glade.get_widget("add_expr"),
			glade.get_widget("del_expr"),
			_("Add Expression"),
			[Field("id", _("ID"), "id_", None, False, auto_gen=True),
			Field("attribute", _("Attribute"), "#uname", ["#uname","#id", "#is_dc"], False),
			Field("operation", _("Operation"), "eq",
				['lt','gt','lte','gte','eq','ne','defined','not_defined'], False),
			Field("value", _("Value"), "", manager.get_all_nodes(), False)],
			self.on_changed)
		self.update()
		
class RscView(View) :

	name = "rscview"
	metaattr_list = None
	param_list = None
	op_list = None
	metaattrs_save = None
	params_save = None
	ops_save = None
	
	attrs_save = None
	meta = None
	def on_apply(self, widget):
		glade = self.glade
		View.on_apply(self, widget)
		new_params = self.param_list.get_data()
		manager.update_attrs("up_rsc_params\n"+self.param,"del_rsc_param",
				   self.params_save, new_params, ["id","name","value"]);
		
		new_ops = self.op_list.get_data()
		manager.update_attrs("up_rsc_full_ops\n10\n"+self.param,"del_rsc_op",
					self.ops_save, new_ops,
					["id", "name","description","interval","timeout",
				 	"start_delay","disabled","role","prereq","on_fail"]);
		
		nv = self.metaattr_list.get_data()
		new_metaattrs = []
		for n in nv.keys():
			new_metaattrs.append({"id": self.param + "_metaattr_" + n,
						"name": n, "value": nv[n]})
		manager.update_attrs("up_rsc_metaattrs\n"+self.param,"del_rsc_metaattr",
				self.metaattrs_save, new_metaattrs, ["id","name","value"]);
				   
	def update(self) :
		glade = self.glade
		(attrs, running_on, metaattrs, params, ops) = \
			manager.get_rsc_info(self.param)

		self.metaattrs_save = metaattrs
		self.params_save = params
		self.ops_save = ops
		
		if attrs != {} :
			glade.get_widget("id").set_text(attrs["id"])
			glade.get_widget("type").set_text(attrs["type"])
			glade.get_widget("class").set_text(attrs["class"])
			glade.get_widget("provider").set_text(attrs["provider"])

		glade.get_widget("runningnodes").set_text(str(running_on))
						
		self.metaattr_list.clear()
		if metaattrs != [] :
			for metaattr in metaattrs:
				self.metaattr_list.insert(metaattr["name"], metaattr["value"])

		self.param_list.clear()
		if params != [] :
			for param in params:
				self.param_list.insert(param)
		
		self.op_list.clear()
		if ops != [] :
			for op in ops:
				self.op_list.insert(op)
		
		attrs = manager.get_rsc_attrs(self.param)
		self.attrs_save = attrs
		View.update(self)
	def on_notebook_event(self, widget, page, page_num) :
		global last_rsc_view_page_num
		last_rsc_view_page_num = page_num
			
	def autofill_param(self, combo, widgets) :
		for param in self.meta.parameters :
			if combo.child.get_text() == param["name"] :
				widgets[2].set_text(param["content"]["default"])

	def autofill_op(self, combo, widgets, combo_name) :			
		if combo_name == "name" :
			for op in self.meta.actions :
				if combo.child.get_text() == op["name"] :
					widgets[2].set_text(op.get("description", ""))
					widgets[3].set_text(op.get("interval", ""))
					widgets[4].set_text(op.get("timeout", ""))
					widgets[5].set_text(op.get("start-delay", ""))
					widgets[6].child.set_text(op.get("disabled", ""))
					widgets[7].child.set_text(op.get("role", ""))
					widgets[8].child.set_text(op.get("prereq", ""))
					widgets[9].child.set_text(op.get("on-fail", ""))
					break
		elif combo_name == "role" :
			for op in self.meta.actions :
				if widgets[1].child.get_text() == op["name"] and combo.child.get_text() == op.get("role", "") :
					widgets[2].set_text(op.get("description", ""))
					widgets[3].set_text(op.get("interval", ""))
					widgets[4].set_text(op.get("timeout", ""))
					widgets[5].set_text(op.get("start-delay", ""))
					widgets[6].child.set_text(op.get("disabled", ""))
					widgets[8].child.set_text(op.get("prereq", ""))
					widgets[9].child.set_text(op.get("on-fail", ""))
					break


	def __init__(self, rsc) :
		View.__init__(self, rsc)
		global last_rsc_view_page_num
		glade = self.glade
		
		(attrs, running_on, metaattrs, params, ops) = manager.get_rsc_info(self.param)
		meta = manager.get_rsc_meta(attrs["class"], attrs["type"], attrs["provider"])
		self.meta = meta
		param_options = None
		op_options = []
		if meta != None :
			param_options = [f["name"] for f in meta.parameters]
			for f in meta.actions :
				if not f["name"] in op_options : op_options.append(f["name"])
			
		self.param_list = ListWithAddDel(glade.get_widget("parameters"),
					    glade.get_widget("addparam"),
					    glade.get_widget("delparam"),
					    _("Add Parameter"),
					    [Field("id", _("ID"), "", None, False, auto_gen=True),
			  		     Field("name", _("Name"), "", param_options, False),
					     Field("value", _("Value"), "", None, True)],
					    self.on_changed,
						self.autofill_param)
				      
		self.op_list = ListWithAddDel(glade.get_widget("operations"),
					 glade.get_widget("addop"),
					 glade.get_widget("delop"),
					 _("Add Operation"),
					    [Field("id", _("ID"), "", None, False, auto_gen=True),
			  		     Field("name", _("Name"), "", op_options, False),
					     Field("description", _("Description"), "", None, True),
					     Field("interval", _("Interval"), "", None, True),
					     Field("timeout", _("Timeout"), "", None, True),
					     Field("start_delay", _("Start Delay"), "0", None, True),
					     Field("disabled", _("Disabled"), "false", ["true","false"], True),
					     Field("role", _("Role"),"Started",["Master","Slave","Started","Stopped"],True),
					     Field("prereq", _("Prereq"), "", ["nothing","quorum","fencing"], True),
					     Field("on_fail", _("On Fail"), "",["ignore","block","stop","restart","fence"], True)],
					 self.on_changed,
					 self.autofill_op)
		
		options_dict = {"is_managed":["default","true","false"],
			"restart_type":["ignore","restart"],
			"multiple_active":["stop_start","stop_only","block"],
			"resource_stickiness":[],
            		"resource_failure_stickiness":[],            
			"description":[],
			"target_role":["started", "stopped", "master", "slave", "default"]}
		self.metaattr_list = NVList(glade.get_widget("attributes"),
					glade.get_widget("addattr"), 
					glade.get_widget("delattr"),
					options_dict,
					self.on_changed)
		notebook = glade.get_widget("notebook")
		notebook.connect("switch-page", self.on_notebook_event)
		notebook.set_current_page(last_rsc_view_page_num)
		self.update()
		
class CompoundRscView(View) :
	name = "compoundrscview"
	metaattr_list = None
	param_list = None
	metaattrs_save = None
	params_save = None
	attrs_save = None
			
	def on_apply(self, widget):
		glade = self.glade
		View.on_apply(self, widget)
		(rid,rtype) = self.param
		new_params = self.param_list.get_data()
		manager.update_attrs("up_rsc_params\n"+rid,"del_rsc_param",
				   self.params_save, new_params, ["id","name","value"]);
				   
		nv = self.metaattr_list.get_data()
		new_metaattrs = []
		for n in nv.keys():
			new_metaattrs.append({"id": rid + "_metaattr_" + n,
						"name": n, "value": nv[n]})
		manager.update_attrs("up_rsc_metaattrs\n"+rid,"del_rsc_metaattr",
				self.metaattrs_save, new_metaattrs, ["id","name","value"]);
		
	def update(self) :
		glade = self.glade
		(rid,rtype) = self.param
		metaattrs = manager.get_rsc_metaattrs(rid)
		self.metaattrs_save = metaattrs
		params = manager.get_rsc_params(rid)
		self.params_save = params
		glade.get_widget("id").set_text(rid)
		glade.get_widget("type").set_text(rtype)

		self.metaattr_list.clear()
		if metaattrs != [] :
			for metaattr in metaattrs:
				self.metaattr_list.insert(metaattr["name"], metaattr["value"])
			
		self.param_list.clear()
		if params != [] :
			for param in params:
				self.param_list.insert(param)
		
		attrs = manager.get_rsc_attrs(rid)
		self.attrs_save = attrs
		View.update(self)
	
	def on_notebook_event(self, widget, page, page_num) :
		global last_compound_view_page_num
		last_compound_view_page_num = page_num
		
	def __init__(self, grp) :
		View.__init__(self, grp)
		glade = self.glade
			
		self.param_list = ListWithAddDel(glade.get_widget("parameters"),
					    glade.get_widget("addparam"),
					    glade.get_widget("delparam"),
					    _("Add Parameter"),
					    [Field("id", _("ID"), "", None, False, auto_gen=True),
			  		     Field("name", _("Name"), "", None, False),
					     Field("value", _("Value"), "", None, True)],
					    self.on_changed)
					    
		options_dict = {"is_managed":["default","true","false"],
			"restart_type":["ignore","restart"],
			"multiple_active":["stop_start","stop_only","block"],
			"ordered":["false", "true"], 
			"resource_stickiness":[],
			"resource_failure_stickiness":[],
			"description":[],
			"target_role":["started", "stopped", "master", "slave", "default"]}
		
		if self.param[1] == _("group"):
			options_dict["collocated"] = ["true","false"] 

		if self.param[1] in [_("clone"), _("master")]:
			options_dict["notify"] = ["false", "true"]
			options_dict["globally_unique"] = ["true","false"]
			options_dict["interleave"] = ["false","true"]
			
		self.metaattr_list = NVList(glade.get_widget("attributes"),
					glade.get_widget("addattr"), 
					glade.get_widget("delattr"),
					options_dict,
					self.on_changed)
		notebook = glade.get_widget("notebook")
		notebook.connect("switch-page", self.on_notebook_event)
		notebook.set_current_page(last_compound_view_page_num)
					
		self.update()
		
class AddNativeDlg :
	param_list = None
	type_list = None
	glade = None
	def update_param_list(self) :
		if self.param_list :
			self.param_list.destory()
		(cur_class, cur_type, cur_provider) = self.get_cur_select()
		meta = manager.get_rsc_meta(cur_class, cur_type, cur_provider)
		param_names = None
		param_descs = None
		if meta != None :
			param_names = [f["name"] for f in meta.parameters]
			param_descs = [f["shortdesc"] for f in meta.parameters]
		
		fields = [Field("id", _("ID"), "id_", None, False, auto_gen = True),
			  Field("name", _("Name"), "", param_names, False),
			  Field("value", _("Value"), "", None, True),
			  Field("shortdesc", _("Description"), "", param_descs, False,False)]
		glade = self.glade	  
		glade.get_widget("is_master").set_active(False)
		glade.get_widget("is_clone").set_active(False)

		param_list = ListWithAddDel(glade.get_widget("parameters"),
				       glade.get_widget("addparam"),
				       glade.get_widget("delparam"),
				       _("Add Parameter"), fields)
		self.param_list = param_list
		if meta != None :
			for param in meta.parameters :
				if param["name"] in [ "master_node_max", "master_max" ] and param["required"] == "1" :
					if not glade.get_widget("is_master").get_active() :
						glade.get_widget("is_master").set_active(True)
					glade.get_widget(param["name"]).set_text(param["value"])
					continue
				if param["name"] in [ "clone_max", "clone_node_max" ] and param["required"] == "1":
					if not glade.get_widget("is_master").get_active() and not glade.get_widget("is_clone").get_active():
						glade.get_widget("is_clone").set_active(True)
					glade.get_widget(param["name"]).set_text(param["value"])
					continue

				if param["required"] == "1" :
					param["id"] = uuid()
					self.param_list.insert(param)
			
	def on_clone_changed(self, widget, glade) :
		
		if glade.get_widget("is_clone").get_active() :
			glade.get_widget("is_master").set_active(False)
			glade.get_widget("advance_id").set_property("sensitive", True)
			glade.get_widget("clone_max").set_property("sensitive", True)
			glade.get_widget("clone_node_max").set_property("sensitive", True)
			glade.get_widget("master_max").set_property("sensitive", False)
			glade.get_widget("master_node_max").set_property("sensitive", False)
		else :
			glade.get_widget("advance_id").set_property("sensitive", False)
			glade.get_widget("clone_max").set_property("sensitive", False)
			glade.get_widget("clone_node_max").set_property("sensitive", False)
			glade.get_widget("master_max").set_property("sensitive", False)
			glade.get_widget("master_node_max").set_property("sensitive", False)
			
	def on_master_changed(self, widget, glade) :
		if glade.get_widget("is_master").get_active() :
			glade.get_widget("is_clone").set_active(False)
			glade.get_widget("advance_id").set_property("sensitive", True)
			glade.get_widget("clone_max").set_property("sensitive", True)
			glade.get_widget("clone_node_max").set_property("sensitive", True)
			glade.get_widget("master_max").set_property("sensitive", True)
			glade.get_widget("master_node_max").set_property("sensitive", True)
		else :
			glade.get_widget("advance_id").set_property("sensitive", False)
			glade.get_widget("clone_max").set_property("sensitive", False)
			glade.get_widget("clone_node_max").set_property("sensitive", False)
			glade.get_widget("master_max").set_property("sensitive", False)
			glade.get_widget("master_node_max").set_property("sensitive", False)
			
	def get_cur_select(self) :
		
		(model, iter) = self.type_list.get_selection().get_selected()
		if iter == None :
			return (None, None, None)
		cur_type = model.get_value(iter, 0)
		cur_provider = model.get_value(iter, 1)
		
		if cur_provider == "stonith" :
			cur_class = "stonith"
			cur_provider = "heartbeat"
		elif cur_provider == "heartbeat":
			cur_class = "heartbeat"
			cur_provider = "heartbeat"
		elif cur_provider == "lsb":
			cur_class = "lsb"
			cur_provider = "heartbeat"
		else :
			cur_class = "ocf"
			cur_provider = string.split(cur_provider,"/")[1]
		return (cur_class, cur_type, cur_provider)	
		
	def on_type_changed(self, type_list) :
		self.update_param_list()
	
	def on_type_list_click(self, type_list, event) :
		if event.type == gtk.gdk._2BUTTON_PRESS :
			(cur_class, cur_type, cur_provider) = self.get_cur_select()
			meta = manager.get_rsc_meta(cur_class, cur_type, cur_provider)
			if meta != None :
				msgbox (meta.longdesc)
			

	
	def init_type_list(self):
		add_column(self.type_list, _("Name"), 0)
		add_column(self.type_list, _("Class")+"/"+_("Provider"), 1)
		add_column(self.type_list, _("Description"), 2)
		for i in range(3) :
			self.type_list.get_column(i).set_sort_column_id(i)
		store = gtk.ListStore(str,str,str)
		store.set_sort_column_id(0, gtk.SORT_ASCENDING)		
		items = {}
		types = []		
		class_list = manager.get_rsc_classes()
		for c in class_list:
			type_list = manager.get_rsc_types(c)
			for t in type_list:
				provider_list = manager.get_rsc_providers(c,t)
				for p in provider_list :
					desc = ""
					meta = manager.get_rsc_meta(c,t,p)
					if meta != None :
						desc = meta.shortdesc
					if c=="heartbeat" :
						p = "heartbeat"
					elif c=="stonith" :
						p = "stonith"
					elif c=="lsb" :
						p = "lsb"
					else :
						p = "ocf/"+p
					if c in ["stonith","lsb"] :
						store.append([t,p,desc])
					elif t in items :
						if p[:3] == "ocf" :
							items[t] = [t,p,desc]
					else :
						items[t] = [t,p,desc]
		for t in items :
			store.append(items[t])
		self.type_list.set_model(store)
		self.type_list.get_selection().select_path((0))
		
	def run(self):	
		global top_window
		dialog = gtk.Dialog(_("Add Native Resource"), window.win_widget, gtk.DIALOG_MODAL,
			(gtk.STOCK_ADD, gtk.RESPONSE_OK, gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL))
		glade = gtk.glade.XML(UI_FILE, "addnativedlg", "haclient")
		layout = glade.get_widget("addnativedlg")
		dialog.vbox.add(layout)
		self.type_list = glade.get_widget("types")
		self.type_list.connect("cursor-changed", self.on_type_changed)
		self.type_list.connect("event", self.on_type_list_click)
		self.init_type_list()
		
		glade.get_widget("is_clone").connect("toggled", self.on_clone_changed, glade)
		glade.get_widget("is_master").connect("toggled", self.on_master_changed, glade)
		
		glade.get_widget("advance_id").set_property("sensitive", False)
		glade.get_widget("clone_max").set_property("sensitive", False)
		glade.get_widget("clone_node_max").set_property("sensitive", False)
		glade.get_widget("master_max").set_property("sensitive", False)
		glade.get_widget("master_node_max").set_property("sensitive", False)

		self.glade = glade
		
		store = gtk.ListStore(str)
		store.append([""])
		for rsc in manager.get_all_rsc_id() :
			if manager.get_rsc_type(rsc) == "group" :
				store.append([rsc])
		glade.get_widget("group").set_model(store)
		glade.get_widget("group").set_text_column(0)
		if window.cur_type == _("group") :
			glade.get_widget("group").child.set_text(window.cur_name)
		self.update_param_list()
		save_top_window = top_window
		top_window = dialog
		while True :
			ret = dialog.run()
			if ret in [gtk.RESPONSE_CANCEL, gtk.RESPONSE_DELETE_EVENT] :
				top_window = save_top_window
				dialog.destroy()
				return None
			else :
				passed = True
				passed = check_entry_value(glade, "id",_("ID"))

				if glade.get_widget("is_clone").get_active() :
					if not check_entry_value(glade, "advance_id",_("ID")): passed = False
					if not check_entry_value(glade, "clone_max"): passed = False
					if not check_entry_value(glade, "clone_node_max"): passed = False
				if glade.get_widget("is_master").get_active() :
					if not check_entry_value(glade, "advance_id",_("ID")): passed = False
					if not check_entry_value(glade, "clone_max"): passed = False
					if not check_entry_value(glade, "clone_node_max"): passed = False
					if not check_entry_value(glade, "master_max"): passed = False
					if not check_entry_value(glade, "master_node_max"): passed = False
				if manager.rsc_exists(glade.get_widget("id").get_text()) :
					msgbox(_("the ID already exists"))
					passed = False

				group_ = glade.get_widget("group").child.get_text()
				if group_ != "" and manager.rsc_exists(group_) and manager.get_rsc_type(group_) != "group":
					msgbox(_("the Group Name is exists"))
					passed = False

				if group_ != "" and glade.get_widget("is_clone").get_active():
					msgbox(_("Clone can't belong to group"))
					passed = False
				if group_ != "" and glade.get_widget("is_master").get_active():
					msgbox(_("Master/Slave can't belong to group"))
					passed = False

				if passed :
					rsc = {}
					rsc["id"] = glade.get_widget("id").get_text()
					(rsc["class"],rsc["type"],rsc["provider"]) = self.get_cur_select()
					rsc["group"] = glade.get_widget("group").child.get_text()
					if rsc["group"] != "" and not manager.rsc_exists(rsc["group"]):
						rsc["new_group"] = "True"
					else:
						rsc["new_group"] = ""
					rsc["params"] = self.param_list.get_data()
					target_role_set = {}
					param_names = []
					for param in rsc["params"] :
						if param["name"] == "target_role" :
							param["id"] = rsc["id"]+"_target_role"
							target_role_set = param
						param_names.append(param["name"])
					repeated_param = False
					for param_name in param_names:
						if param_names.count(param_name) > 1:
							repeated_param = True
							break
					if repeated_param:
						msgbox(_("Parameter \""+param_name+"\" is defined repeatedly"))
						continue
					if glade.get_widget("is_clone").get_active() :
						rsc["advance"] = "clone"
					elif glade.get_widget("is_master").get_active() :
						rsc["advance"] = "master"
					else :
						rsc["advance"] = ""

					if (rsc["group"] != "" or rsc["advance"] != "") and target_role_set != {}:
						retval = confirmbox(_("Recommended:")+"\n"
							+_("Select \"Clear\" to remove the set.")+"\n"
							+_("It will inherit the attribute from its parent resource."),
							(gtk.STOCK_CLEAR, gtk.RESPONSE_NO,
							gtk.STOCK_YES, gtk.RESPONSE_YES,
							gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL),
							_("Are you sure you want to set \"target_role\"?"))
						if retval == gtk.RESPONSE_CANCEL:
							continue
						if retval == gtk.RESPONSE_NO:
							rsc["params"].remove(target_role_set)

					rsc["advance_id"] = glade.get_widget("advance_id").get_text()
					rsc["clone_max"] = glade.get_widget("clone_max").get_text()
					rsc["clone_node_max"] = glade.get_widget("clone_node_max").get_text()
					rsc["master_max"] = glade.get_widget("master_max").get_text()
					rsc["master_node_max"] = glade.get_widget("master_node_max").get_text()
					
					manager.add_native(rsc)
					top_window = save_top_window
					dialog.destroy()
					return None
		
	

class MainWindow :
	'''
	Main UI window to show information to user and get users input
	'''
	win_widget = None
	actiongroup = None
	uimanager = None
	cur_type = None
	cur_status = None
	cur_name = None
	cur_view = None
	view_widget = None
	statusbar = None
	tree = None
	
	# utility functions
	def set_action_sensitive(self, action, sensitive) :
		self.actiongroup.get_action(action).set_property("sensitive", sensitive)

	# system event handler
	def on_delete_event(self, widget, event, data=None) :
		self.on_quit(None)
		return False
	
	def update_ui(self) :
		all_rsc_type = [_("native"),_("group"),_("clone"),_("master")]
		self.set_action_sensitive('logout',manager.connected)
		self.set_action_sensitive('login',not manager.connected)
		self.set_action_sensitive('addrsc',manager.connected)
		
		self.set_action_sensitive('delrsc',
			manager.connected
			and self.cur_type in [_("native"),_("group"),_("clone"),
				_("location"),_("order"),_("colocation"),_("master")])

		self.set_action_sensitive('standby',
			manager.connected
			and self.cur_type in [_("node")]
			and string.find(self.cur_status, _("standby")) == -1
			and string.find(self.cur_status, _("never started")) == -1)

		self.set_action_sensitive('active',
			manager.connected
			and self.cur_type in [_("node")]
			and string.find(self.cur_status, _("standby")) != -1
			and string.find(self.cur_status, _("never started")) == -1)

		self.set_action_sensitive('cleanuprsc',
			manager.connected
			and self.cur_type in all_rsc_type)

		self.set_action_sensitive('startrsc',
			manager.connected
			and self.cur_type in all_rsc_type)
		
		self.set_action_sensitive('stoprsc',
			manager.connected
			and self.cur_type in all_rsc_type)

		self.set_action_sensitive('defaultrsc',
			manager.connected
			and self.cur_type in all_rsc_type)

		self.set_action_sensitive('uprsc',
			manager.connected
			and self.cur_type in [_("native")])

		self.set_action_sensitive('downrsc',
			manager.connected
			and self.cur_type in [_("native")])

	# functions
	def update(self) :
		self.tree.update()
		self.statusbar.pop(2)
		return False
	
	def can_change_view(self) :
		if not self.cur_view.changed :
			return True
		ret = confirmbox(_("The data of current view have been changed.")+"\n"+_("Apply the changes?"),
			(gtk.STOCK_YES, gtk.RESPONSE_YES, 
			gtk.STOCK_NO, gtk.RESPONSE_NO,
			gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
		if ret == gtk.RESPONSE_YES :
			self.cur_view.on_apply(self.cur_view.widget)
			return True
		if ret == gtk.RESPONSE_NO :
			return True
		return False
	
	def select_view(self, type, name, status) :
		for child in self.view_widget.get_children() :
			self.view_widget.remove(child)

		self.cur_type, self.cur_name, self.cur_status = type, name, status
		self.update_ui()
		if type == "cluster" :
			self.cur_view = ClusterView()
		elif type == "node" and status not in [_("never started"),_("ping node"),_("unknown type")]:
			self.cur_view = NodeView(name)
		elif type in [_("native")] :
			self.cur_view = RscView(name)
		elif type == _("location") :
			self.cur_view = LocationView(name)
		elif type == _("order") :
			self.cur_view = OrderView(name)
		elif type == _("colocation") :
			self.cur_view = ColocationView(name)
		elif type in [ _("clone"), _("group"), _("master")] :
			self.cur_view = CompoundRscView((name,type))
		else :
			self.cur_view = View()
		self.view_widget.add(self.cur_view.widget)
		self.cur_view.on_after_show()
			
	# ui event handler	
	def on_quit(self, action) :
		gtk.main_quit()
	
	def on_login(self, action) :
		dialog = gtk.Dialog(_("Login"),self.win_widget, gtk.DIALOG_MODAL,
			(gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
		
		glade = gtk.glade.XML(UI_FILE, "logindlg_main", "haclient")
		main = glade.get_widget("logindlg_main")
		dialog.vbox.add(main)
		(server,user) = manager.last_login_info()
		glade.get_widget("server").set_text(server)
		glade.get_widget("username").set_text(user)
		glade.get_widget("password").set_text("")
		glade.get_widget("server").set_activates_default(True)
		glade.get_widget("username").set_activates_default(True)
		glade.get_widget("password").set_activates_default(True)
		dialog.set_default_response(gtk.RESPONSE_OK)
		ret =  dialog.run()
		server = glade.get_widget("server").get_text()
		user = glade.get_widget("username").get_text()
		password = glade.get_widget("password").get_text()
		
		dialog.destroy()

		if ret == gtk.RESPONSE_OK:
			if not manager.login(server, user, password):
				msgbox(manager.failed_reason)
			else:
				manager.save_login_info(server,user)
	
		self.update_ui()

	def on_logout(self, action) :
		manager.logout()
		self.update_ui()
	
	def on_test(self, action) :
		print uuid()
			 
	def on_standby(self, action) :
		if confirmbox(_("Make") +" " +self.cur_name + " " +_("standby")+"?") == gtk.RESPONSE_YES:
			manager.do_cmd("standby\n"+self.cur_name + "\n" + "on")

	def on_active(self, action) :
		if confirmbox(_("Make") +" " +self.cur_name + " " + _("active")+"?") == gtk.RESPONSE_YES :
			manager.do_cmd("standby\n"+self.cur_name + "\n" + "off")

	def on_add_item(self, action) :
		if self.cur_type in [_("constraints"), _("locations"), _("location")]:
			add_type = _("location") 	
		elif self.cur_type in [_("orders"), _("order")]:
			add_type = _("order")
		elif self.cur_type in [_("colocations"), _("colocation")]:
			add_type = _("colocation")
		else:
			add_type = _("native")
		new_type = kvbox(_("The type of new item"),
				[Field("type",_("Item Type"),add_type,
				[_("native"), _("group"), _("location"),_("order"), _("colocation")])])
		if new_type == None :
			return
		if new_type["type"] == _("native"):
			dlg = AddNativeDlg()
			dlg.run()
		elif new_type["type"] == _("group") : 	 
			group = kvbox(_("Add Resource Group"),
				[Field("id",_("ID"),"group_"),
				Field("ordered",_("Ordered"),"true", ["true","false"],False),
				Field("collocated",_("Collocated"),"true", ["true","false"],False)])
			if group != None :
				self.cur_name = group["id"]
				self.cur_type = _("group")
				manager.cache_update(group["id"]+"\nordered",group["ordered"])
				manager.cache_update(group["id"]+"\ncollocated",group["collocated"])
				dlg = AddNativeDlg()
				dlg.run()
				
		elif new_type["type"] == _("location") :
			location = kvbox(_("Add Location Constraint"),
				[Field("id",_("ID"),"location_",None, False),
				 Field("rsc",_("Resource"),None, manager.get_all_rsc_id(),False)])
			if location != None :
				location["score"] = '0'
				location["boolean_op"] = ''
				location["exprs"] = []
				manager.update_constraint("rsc_location", location)
		elif new_type["type"] == _("order") :
			order = kvbox(_("Add Order Constraint"),
				[Field("id",_("ID"),"order_",None,False),
				 Field("from",_("From"),None, manager.get_all_rsc_id(),False),
				 Field("type",_("Type"),None, ["before","after"], False),
				 Field("to",_("To"),None, manager.get_all_rsc_id(), False)])
			if order != None :
				manager.update_constraint("rsc_order", order)
		elif new_type["type"] == _("colocation") :
			colocation = kvbox(_("Add Colocation Constraint"),
				[Field("id",_("ID"),"colocation_",None,False),
				 Field("from",_("From"),None, manager.get_all_rsc_id(),False),
				 Field("to",_("To"),None, manager.get_all_rsc_id(), False),
				 Field("score",_("Score"),None, ["INFINITY","0","-INFINITY"], False)])
			if colocation != None :
				manager.update_constraint("rsc_colocation", colocation)
			
	def on_del_item(self, action) :
		if confirmbox(_("Delete") + " "+self.cur_type + " " + self.cur_name + "?") == gtk.RESPONSE_YES:
			if self.cur_type in [_("group"),_("clone"),_("master")] :
				manager.do_cmd("del_rsc\n"+self.cur_name)
			elif self.cur_type == _("native") :
				if self.cur_name not in manager.parent :
					manager.do_cmd("del_rsc\n"+self.cur_name)
				try :	
					parent = manager.parent[self.cur_name] 
				except KeyError : # the self.cur_name does not exist in manager.parent
					parent = self.cur_name
				if len(manager.get_rsc_sub_rsc(parent)) == 1 :
					manager.do_cmd("del_rsc\n"+parent)
				else :
					manager.do_cmd("del_rsc\n"+self.cur_name)
			elif self.cur_type == _("location") :
				manager.do_cmd("del_co\nrsc_location\n"+self.cur_name)
			elif self.cur_type == _("order") :
				manager.do_cmd("del_co\nrsc_order\n"+self.cur_name)
			elif self.cur_type == _("colocation") :
				manager.do_cmd("del_co\nrsc_colocation\n"+self.cur_name)
			if manager.failed_reason != "" :
				msgbox(manager.failed_reason)

	def on_item_action(self, action) :
		if action.get_name() == "startrsc" :
			target_role = "started"
		elif action.get_name() == "stoprsc" :
			target_role = "stopped"
		else :
			target_role = "#default"
				
		(attrs, running_on, metaattrs, params, ops) = \
			manager.get_rsc_info(self.cur_name)
		for param in params :
			if param["name"] == "target_role" and param["value"] == target_role :
				return

		for subrsc in manager.get_rsc_sub_rsc(self.cur_name) :
			(attrs, running_on, metaattrs, params, ops) = \
				manager.get_rsc_info(subrsc)
			for metaattr in metaattrs :
				if metaattr["name"] == "target_role":
					retval = confirmbox(_("Caused by your previous action, this attribute has been set to \"")
								+metaattr["value"]
								+_("\".")+"\n\n"
								+_("Recommended:")+"\n"
								+_("Select \"Clear\" to remove the set.")+"\n"
								+_("The sub-resource will inherit this attribute from current resource."),
								(gtk.STOCK_CLEAR, gtk.RESPONSE_NO,
								gtk.STOCK_YES, gtk.RESPONSE_YES,
								gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL),
								_("Do you want to retain the \"target_role\" set for sub-resource \"")
								+subrsc+_("\"?"))
					if retval == gtk.RESPONSE_CANCEL:
						return	
					if retval == gtk.RESPONSE_NO:
						new_metaattrs = metaattrs[:]
						new_metaattrs.remove(metaattr)
						manager.update_attrs("up_rsc_metaattrs\n"+subrsc ,"del_rsc_metaattr",
							metaattrs, new_metaattrs, ["id","name","value"]);
		manager.do_cmd("set_target_role\n%s\n%s"%(self.cur_name,target_role))
	
	def on_item_move(self, action) :
		if action.get_name() == "uprsc" :
			manager.do_cmd("move_rsc\n%s\nup"%self.cur_name)
		elif action.get_name() == "downrsc" :
			manager.do_cmd("move_rsc\n%s\ndown"%self.cur_name)
	
	def on_item_cleanup(self, action) :
		if self.cur_type == _("native") :
			manager.do_cmd("cleanup_rsc\n"+self.cur_name)
		elif self.cur_type in [_("group"),_("clone"),_("master")] :
			for rsc in manager.get_rsc_sub_rsc(self.cur_name) :
				manager.do_cmd("cleanup_rsc\n"+rsc)

	def load_icon(self, name, file) :
		icons = gtk.IconFactory()
		icons.add(name,gtk.IconSet(gtk.gdk.pixbuf_new_from_file(file)))
		icons.add_default()

	# constructor	
	def __init__(self) :
		# create window
		win_widget = gtk.Window()
		win_widget.connect("delete_event", self.on_delete_event)
		win_widget.set_title(_("Linux HA Management Client"))
		win_widget.set_size_request(750, 600)
		win_widget.set_icon_from_file("@HA_DATADIR@/heartbeat-gui/ha.png")

		# add the icons to gtk stock
		self.load_icon("ha-login","@HA_DATADIR@/heartbeat-gui/login.png")
		self.load_icon("ha-logout","@HA_DATADIR@/heartbeat-gui/logout.png")
		self.load_icon("ha-exit","@HA_DATADIR@/heartbeat-gui/exit.png")
		self.load_icon("ha-standby-node","@HA_DATADIR@/heartbeat-gui/standby-node.png")
		self.load_icon("ha-active-node","@HA_DATADIR@/heartbeat-gui/active-node.png")
		self.load_icon("ha-add-resource","@HA_DATADIR@/heartbeat-gui/add-resource.png")
		self.load_icon("ha-remove-resource","@HA_DATADIR@/heartbeat-gui/remove-resource.png")
		self.load_icon("ha-start-resource","@HA_DATADIR@/heartbeat-gui/start-resource.png")
		self.load_icon("ha-stop-resource","@HA_DATADIR@/heartbeat-gui/stop-resource.png")
		self.load_icon("ha-cleanup-resource","@HA_DATADIR@/heartbeat-gui/cleanup-resource.png")
		self.load_icon("ha-default-resource","@HA_DATADIR@/heartbeat-gui/default-resource.png")
		self.load_icon("ha-up-resource","@HA_DATADIR@/heartbeat-gui/up-resource.png")
		self.load_icon("ha-down-resource","@HA_DATADIR@/heartbeat-gui/down-resource.png")
		self.load_icon("ha-master-resource","@HA_DATADIR@/heartbeat-gui/master-resource.png")
		self.load_icon("ha-slave-resource","@HA_DATADIR@/heartbeat-gui/slave-resource.png")

		# create ui-manager
		ui_xml = '''
		<ui>
			<menubar name="menubar">
				<menu action="connection">
					<menuitem action="login"/>
					<menuitem action="logout"/>
					<menuitem action="quit"/>
				</menu>
				<menu action="resource">
					<menuitem action="addrsc"/>
					<menuitem action="delrsc"/>
					<menuitem action="cleanuprsc"/>
					<menuitem action="startrsc"/>
					<menuitem action="stoprsc"/>
					<menuitem action="defaultrsc"/>
					<menuitem action="uprsc"/>
					<menuitem action="downrsc"/>
				</menu>
				<menu action="node">
					<menuitem action="standby"/>
					<menuitem action="active"/>
				</menu>
			</menubar>
			<popup name="resource_popup">
				<menuitem action="addrsc"/>
				<menuitem action="delrsc"/>
				<menuitem action="cleanuprsc"/>
				<menuitem action="startrsc"/>
				<menuitem action="stoprsc"/>
				<menuitem action="defaultrsc"/>
				<menuitem action="uprsc"/>
				<menuitem action="downrsc"/>
			</popup>
			<popup name="connection_popup">
				<menuitem action="login"/>
				<menuitem action="logout"/>
				<menuitem action="quit"/>
			</popup>
			<popup action="node_popup">
				<menuitem action="standby"/>
				<menuitem action="active"/>
			</popup>
			<toolbar name="toolbar">
				<toolitem action="login"/>
				<toolitem action="logout"/>
				<separator/>
				<toolitem action="addrsc"/>
				<toolitem action="delrsc"/>
				<toolitem action="cleanuprsc"/>
				<toolitem action="startrsc"/>
				<toolitem action="stoprsc"/>
				<toolitem action="defaultrsc"/>
				<toolitem action="uprsc"/>
				<toolitem action="downrsc"/>
				<separator/>
				<toolitem action="standby"/>
				<toolitem action="active"/>
				<separator/>
				<toolitem action="quit"/>
			</toolbar>
		</ui>'''
		uimanager = gtk.UIManager()
		actiongroup = gtk.ActionGroup('haclient')
		actiongroup.add_actions([
			('connection', None, _('Connection')),
			('login', "ha-login", _('Login...'), None,_('login to cluster'), self.on_login),
			('logout', "ha-logout", _('Logout'), None,_('logout from cluster'), self.on_logout),
			('quit', "ha-exit", _('Quit'), None,_('Quit the Program'), self.on_quit),
			('resource', None, _('Resources')),
			('addrsc', "ha-add-resource", _('Add New Item'), None,_('add new item'), self.on_add_item),
			('delrsc', "ha-remove-resource", _('Delete'), None,_('delete current item'), self.on_del_item),
			('cleanuprsc', "ha-cleanup-resource", _('Cleanup resource'), None,_('cleanup resource'), self.on_item_cleanup),
			('startrsc', "ha-start-resource", _('Start'), None,_('start resource'), self.on_item_action),
			('stoprsc', "ha-stop-resource", _('Stop'), None,_('stop resource'), self.on_item_action),
			('defaultrsc', "ha-default-resource", _('Default'), None,_('work as default'), self.on_item_action),
			('uprsc', "ha-up-resource", _('Move Up'), None,_('move the resource up in the group'), self.on_item_move),
			('downrsc', "ha-down-resource", _('Move Down'), None,_('move the resource down in the group'),
				 self.on_item_move),
			('node', None, _('Nodes')),
			('standby', "ha-standby-node", _('Standby'), None,_('make the node standby'), self.on_standby),
			('active', "ha-active-node", _('Active'), None,_('make the node active'), self.on_active)
			])
		uimanager.insert_action_group(actiongroup, 0)
		uimanager.add_ui_from_string(ui_xml)

		# put componets to window
		vbox = gtk.VBox()
		win_widget.add(vbox)
		
		menubar = uimanager.get_widget('/menubar')
		vbox.pack_start(menubar, False)
		
		toolbar = uimanager.get_widget('/toolbar')
		toolbar.set_style(gtk.TOOLBAR_ICONS)
		vbox.pack_start(toolbar, False)
		
		glade = gtk.glade.XML(UI_FILE, "mainwin_main", "haclient")
		main = glade.get_widget("mainwin_main")
		vbox.pack_start(main, True)
		
		self.statusbar = gtk.Statusbar()
		vbox.pack_end(self.statusbar, False)
		
		# show the window
		win_widget.show_all()
		# keep some widgets
		self.win_widget = win_widget
		self.actiongroup = actiongroup
		self.uimanager = uimanager
		self.tree = Tree(glade.get_widget("mainwin_tree"))
		self.view_widget = glade.get_widget("mainwin_view")
		self.update_ui()
		self.statusbar.push(0,_("Not Connected"))

class Manager :
	'''
	Manager will connect to mgmtd and control the main window
	'''
	connected = False
	window = None
	selected_type = None
	server = None
	username = None
	password = None
	cache = {}
	no_update_cache = {}
	parent = {}
	io_tag = None
	update_timer = -1
	active_nodes = []
	all_nodes = []
	failed_reason = ""
		
	# cache functions

	def cache_lookup(self, key) :
		if self.cache.has_key(key) :
			return self.cache[key]
		if self.no_update_cache.has_key(key) :
			return self.no_update_cache[key]
		return None
			
	def cache_update(self, key, data, keep_in_cache = False) :
		if not keep_in_cache :
			self.cache[key] = data
		else :
			self.no_update_cache[key] = data
			
	def cache_delkey(self, key) :
		if self.cache.has_key(key) : 
			del self.cache[key]
			
	def cache_clear(self) :
		self.cache.clear()
		
	# internal functions	
	def split_attr_list(self, attrs, keys) :
		attr_list = []
		if attrs != None :
			for i in range(0, len(attrs), len(keys)) :
				attr = {}
				for j in range (0, len(keys)) :
					attr[keys[j]] = attrs[i+j]
				attr_list.append(attr)
		return attr_list
		
	def run(self) :
		gtk.main()
		if self.connected :
			mgmt_disconnect()
	
	# connection functions
	def last_login_info(self) :
		save_path = os.environ["HOME"]+"/.haclient"
		if not os.path.exists(save_path) :
			return ("127.0.0.1","hacluster")
		try:
			return pickle.load(file(save_path,"r"))
		except:
			return ("127.0.0.1", "hacluster")	
	def save_login_info(self, server, user) :
		save_path = os.environ["HOME"]+"/.haclient"
		try:
			pickle.dump((server,user), file(save_path,"w"))
		except:
			os.remove(save_path)
		return
		
	def login(self, server, username, password) :
		# connect to one of the cluster
		self.failed_reason = ""
		self.server = server
		port = ""
		if string.find(server, ":") != -1 :
			server,port = string.split(server,":")
		ip = socket.gethostbyname(server)
		ret = mgmt_connect(ip, username, password, port)
		if ret != 0 :
			if ret == -1 :
				self.failed_reason = _("Can't connect to server")
			elif ret == -2 :
				self.failed_reason =\
				 _("Failed in the authentication.\n User Name or Password may be wrong." \
				"\n or the user doesn't belong to haclient group")
			mgmt_disconnect()
			return False
		window.statusbar.pop(1)	
		window.statusbar.push(1,_("Connected to ")+server)
		self.connected = True
		self.username = username
		self.password = password

		window.statusbar.push(2,_("Updating data from server..."))
		self.update_timer = gobject.timeout_add(200, self.update)
		
		self.do_cmd("regevt\nevt:cib_changed")
		self.do_cmd("regevt\nevt:disconnected")
		
		fd = mgmt_inputfd()
		self.io_tag = gobject.io_add_watch(fd, gobject.IO_IN, self.on_event, None)
		return True
	
	def query(self, query, keep_in_catch = False) :
		result = self.cache_lookup(query)
		if  result != None :
			return 	result
		result = self.do_cmd(query)
		self.cache_update(query, result, keep_in_catch)
		return result
		
	def do_cmd(self, command) :
		self.failed_reason = ""
		ret_str = mgmt_sendmsg(command)
		if ret_str == None :
			debug(str(string.split(command, "\n"))+":None")
			self.failed_reason = "return None"
			return None
 		while len(ret_str)>=4 and ret_str[:4] == "evt:" :
			gobject.idle_add(self.on_event, None, None, ret_str)
 			ret_str = mgmt_recvmsg()
			if ret_str == None :
				debug(str(string.split(command, "\n"))+":None")
				self.failed_reason = "return None"
				return None

		ret_list = string.split(ret_str, "\n")
		if ret_list[0] != "ok" :
			debug(str(string.split(command, "\n"))+":"+ str(ret_list))
			if len(ret_list) > 1 :
				self.failed_reason = string.join(ret_list[1:],",")
			return None
		debug(str(string.split(command, "\n"))+":"+ str(ret_list))
		return ret_list[1:]
		
		
	def logout(self) :
		mgmt_disconnect()
		gobject.source_remove(self.io_tag)
		self.connected = False
		window.update()
		window.statusbar.pop(1)
		
	# event handler	
	def on_reconnect(self) :
		if self.all_nodes == [] :
			window.statusbar.pop(1)
			return False
		for server in self.all_nodes :
			if self.login(self.server, self.username, self.password) :
				return False
		return True

	def process_event(self, event) :
		if event == "evt:cib_changed" :
			if self.update_timer != -1 :
				gobject.source_remove(self.update_timer)
			else :
				window.statusbar.push(2,_("Updating data from server..."))
			self.update_timer = gobject.timeout_add(200, self.update)

		elif event == None or event == "evt:disconnected" :
			self.logout()
			for server in self.active_nodes :
				if self.login(self.server, self.username, self.password) :
					break
			else :
				window.statusbar.push(1,_("Reconnecting..."))
				gobject.timeout_add(1000, self.on_reconnect)
	
	def on_event(self, source, condition, event_str) :
		if event_str == None : 	# called by gtk
			event = mgmt_recvmsg()
			log("on_event:"+str(event))
			self.process_event(event)
			return True
		else :					# called by do_cmd
			event = event_str 
			log("on_event: from message queue: "+str(event))
			self.process_event(event)
			return False

	def update(self) :
		self.cache_clear()
		self.parent = {}
		window.update()	
		gc.collect()
		self.update_timer = -1

	# cluster functions		
	def get_crm_metadata(self, crm_cmd) :
		if crm_cmd == None :
			return None
		lines = self.query("crm_metadata\n%s"%(crm_cmd),True)

		if lines == None :
			return None
		meta_data = ""
		for line in lines :
			if len(line)!= 0 :
				meta_data = meta_data + line + "\n"

		try :
			doc_xml = parseString(meta_data).documentElement
		except xml.parsers.expat.ExpatError:
			debug("fail to parse the metadata of "+rsc_type)
			return None
			
		meta = RAMeta()
		meta.name = doc_xml.getAttribute("name")

		meta.version = ""
		version_xml = doc_xml.getElementsByTagName("version")
		if version_xml != [] and version_xml[0] in doc_xml.childNodes :
			meta.version = version_xml[0].childNodes[0].data

		meta.longdesc = self.get_locale_desc(doc_xml, "longdesc");
		meta.shortdesc = self.get_locale_desc(doc_xml, "shortdesc");
		
		meta.parameters = []
		for param_xml in doc_xml.getElementsByTagName("parameter") :
			param = {}
			param["name"] = param_xml.getAttribute("name")
			param["unique"] = param_xml.getAttribute("unique")
			param["longdesc"] = self.get_locale_desc(param_xml, "longdesc");
			param["shortdesc"] = self.get_locale_desc(param_xml, "shortdesc");
			content_xml = param_xml.getElementsByTagName("content")[0]
			content = {}
			content["type"] = content_xml.getAttribute("type")
			content["default"] = content_xml.getAttribute("default")
			if content["type"] == "enum":
				values_tag = "Allowed values:"
				index = param["longdesc"].rfind(values_tag)
				if index != -1:
					strings = param["longdesc"][index+len(values_tag):].split(",")
					content["values"] = []
					for string in strings:
						content["values"].append(string.strip())
				
			param["content"] = content
			meta.parameters.append(param)
		return meta


	def get_cluster_config(self) :
		
		hb_attr_names = ["apiauth","auto_failback","baud","debug","debugfile",
				 "deadping","deadtime","hbversion","hopfudge",
				 "initdead","keepalive","logfacility","logfile",
				 "msgfmt","nice_failback","node","normalpoll",
			 	"stonith","udpport","warntime","watchdog", "cluster"]
				  
		values = manager.query("hb_config")
		if values == None :
			return None
		config = dict(zip(hb_attr_names,values))
				
		value = manager.query("crm_config\nhave_quorum")
		if value != None and value != []:
			config["have_quorum"] = value[0]

		return config

	def get_crm_config(self, metadata):
		config = {}
		for parameter in metadata.parameters:
			value = manager.query("crm_config\n%s"% \
					str(parameter["name"]))
			if value == None or value == []:
				continue
			if value == [""]:
				config[parameter["name"]] = str(parameter["content"]["default"])
			else:
				config[parameter["name"]] = value[0]
		return config
		
	def update_crm_config(self, metadata, new_crm_config) :
		for k,v in new_crm_config.iteritems() :
			value = manager.query("crm_config\n"+str(k))
			if value == [""]:
				for parameter in metadata.parameters:
					if parameter["name"] == k:
						cur_value = str(parameter["content"]["default"])
			else:
				cur_value = value[0]
			if cur_value != v :
				self.do_cmd("up_crm_config\n"+str(k)+"\n"+v)
				if self.failed_reason != "" :
					msgbox(self.failed_reason)

				
	# node functions
	def get_dc(self):
		return self.query("dc")

	def get_all_nodes(self) :
		self.all_nodes = self.query("all_nodes")
		return self.all_nodes

	def get_nodetype(self, node):
		return self.query("node_type\n%s"%node)
	
	def get_active_nodes(self):
		self.active_nodes = self.query("active_nodes")
		return self.active_nodes
	
	def get_crm_nodes(self):
		return self.query("crm_nodes")
				
	def get_node_config(self, node) :
		node_attr_names = ["uname", "online","standby", "unclean", "shutdown",
				   "expected_up","is_dc","type"]
				  
		values = manager.query("node_config\n%s"%node)
		if values == None :
			return None
		config = dict(zip(node_attr_names,values))
		
		return config

	def get_running_rsc(self, node) :
		return self.query("running_rsc\n%s"%node)	

	# resource functions
	def add_group(self, group) :
		if len(group["id"]) == 0 :
			msgbox (_("the ID can't be empty"))
			return
		if self.rsc_exists(group["id"]):
			msgbox (_("the ID already exists"))
			return
		cmd = "add_grp\n"+group["id"]
		for param in group["params"] :
			cmd += "\n"+param["id"]
			cmd += "\n"+param["name"]
			cmd += "\n"+param["value"]

		self.do_cmd(cmd)
		if self.failed_reason != "" :
			msgbox(self.failed_reason)
		
	def add_native(self, rsc) :
		if self.rsc_exists(rsc["id"]) :
			msgbox (_("the ID already exists"))
			return
			
		cmd = "add_rsc"
		cmd += "\n"+rsc["id"]
		cmd += "\n"+rsc["class"]
		cmd += "\n"+rsc["type"]
		cmd += "\n"+rsc["provider"]
		cmd += "\n"+rsc["group"]
		cmd += "\n"+rsc["advance"]
		cmd += "\n"+rsc["advance_id"]
		cmd += "\n"+rsc["clone_max"]
		cmd += "\n"+rsc["clone_node_max"]
		cmd += "\n"+rsc["master_max"]
		cmd += "\n"+rsc["master_node_max"]
		cmd += "\n"+rsc["new_group"]
		for param in rsc["params"] :
			cmd += "\n"+param["id"]
			cmd += "\n"+param["name"]
			cmd += "\n"+param["value"]
		self.do_cmd(cmd)
		if self.failed_reason != "" :
			msgbox (self.failed_reason)
		group_id = rsc["group"]
		if group_id != None and group_id != "" :
			ordered = self.cache_lookup(group_id+"\nordered")
			if ordered != None :
				new_metaattrs = [{"id": group_id + "_metaattr_ordered" ,
                                                "name": "ordered", "value": ordered}]
				manager.update_attrs("up_rsc_metaattrs\n"+group_id,"del_rsc_metaattr",
						[], new_metaattrs, ["id","name","value"]);
				self.cache_delkey(group_id+"\nordered")
			collocated = self.cache_lookup(group_id+"\ncollocated")
			if collocated != None :
				new_metaattrs = [{"id": group_id + "_metaattr_collocated" ,
                                                "name": "collocated", "value": collocated}]
				manager.update_attrs("up_rsc_metaattrs\n"+group_id,"del_rsc_metaattr",
						[], new_metaattrs, ["id","name","value"]);
				self.cache_delkey(group_id+"\ncollocated")
				
	def get_all_rsc_id(self) :
		return self.query("all_rsc")

	def get_rsc_type(self, rsc_id) :
		return self.query("rsc_type\n"+rsc_id)[0]
	
	def get_rsc_status(self, rsc_id) :
		return self.query("rsc_status\n"+rsc_id)[0]

	def get_rsc_running_on(self, rsc_id) :
		return self.query("rsc_running_on\n"+rsc_id)

	def get_rsc_sub_rsc(self, rsc_id) :
		sub_rscs = self.query("sub_rsc\n"+rsc_id)
		if sub_rscs != None :
			for sub_rsc in sub_rscs :
				self.parent[sub_rsc] = rsc_id
		return sub_rscs

	def get_rsc_info(self, rsc) :
		rsc_attr_names = ["id", "description", "class", "provider", 
				"type", "is_managed","restart_type",
				"multiple_active","resource_stickiness",
                "resource_failure_stickiness"]
				
		op_attr_names = ["id", "name","description","interval","timeout",
				 "start_delay","disabled","role","prereq","on_fail"]
		param_attr_names = ["id", "name", "value"]
		meta_attr_names = ["id", "name", "value"]
		
		attr_list = self.query("rsc_attrs\n%s"%rsc)
		attrs = {}
		if attr_list != None :
			attrs = dict(zip(rsc_attr_names, attr_list))
		
		running_on = self.query("rsc_running_on\n%s"%rsc)

		raw_metaattrs = self.query("rsc_metaattrs\n%s"%rsc)
		metaattrs = self.split_attr_list(raw_metaattrs, meta_attr_names)

		raw_params = self.query("rsc_params\n%s"%rsc)

		params = self.split_attr_list(raw_params, param_attr_names)
		
		raw_ops = self.query("rsc_full_ops\n%s"%rsc)
		raw_ops = raw_ops[1:]
		ops = self.split_attr_list(raw_ops, op_attr_names)

		return (attrs, running_on, metaattrs, params, ops)

	def get_rsc_metaattrs(self, rsc) :
		meta_attr_names = ["id", "name", "value"]
		raw_metaattrs = self.query("rsc_metaattrs\n%s"%rsc)
		return self.split_attr_list(raw_metaattrs, meta_attr_names)
	def get_rsc_params(self, rsc) :
		param_attr_names = ["id", "name", "value"]
		raw_params = self.query("rsc_params\n%s"%rsc)
		return self.split_attr_list(raw_params, param_attr_names)
	
	def get_rsc_attrs(self, rsc) :
		rsc_attr_names = ["id", "description", "class", "provider",
				"type", "is_managed","restart_type",
				"multiple_active","resource_stickiness",
                "resource_failure_stickiness"]
		grp_attr_names = ["id", "description", "is_managed","restart_type",
				"multiple_active","resource_stickiness",
                "resource_failure_stickiness",
				"ordered", "collocated"]
		clone_attr_names = ["id", "description", "is_managed","restart_type",
				"multiple_active","resource_stickiness",
                "resource_failure_stickiness", 
				"notify", "globally_unique", "ordered",
				"interleave"]
		attr_list = self.query("rsc_attrs\n%s"%rsc)
		attrs = {}
		rtype = self.query("rsc_type\n"+rsc)[0]
		if rtype == "native" :
			attrs = dict(zip(rsc_attr_names, attr_list))
		elif rtype == "group" :				
			attrs = dict(zip(grp_attr_names, attr_list))
		elif rtype in ["clone","master"] :
			attrs = dict(zip(clone_attr_names, attr_list))
		for n in attrs.keys() :
			if attrs[n] == "#default" or attrs[n] == "" :
				del attrs[n]
		return attrs

	def get_locale_desc(self, node, tag) :
		desc_en = ""
		desc_match = ""
		
		(lang, encode) = locale.getlocale()
		if lang == None:
			lang = "en"
		else:
			lang = string.lower(lang)
		if encode == None:	
			encode = ""
		else:	
			encode = string.lower(encode)
		
		for child in node.childNodes :
			if child.nodeType != node.ELEMENT_NODE :
				continue
			if child.tagName != tag :
				continue
			if len(child.childNodes) == 0:
				break
			langtag = string.lower(child.getAttribute("lang"))
			if langtag == "" :
				desc_en = child.childNodes[0].data
			else :
				langtag = string.split(langtag, ".")
				if string.find(langtag[0], "en") != -1 :
					desc_en = child.childNodes[0].data
				if len(langtag) == 1 and lang == langtag[0] :
					desc_match = child.childNodes[0].data
				if len(langtag) == 2 :	
					if lang == langtag[0] and encode == langtag[1] :
						desc_match = child.childNodes[0].data	
		if desc_match != "" :
			return desc_match
		return desc_en				
		
			
	def get_rsc_meta(self, rsc_class, rsc_type, rsc_provider) :
		if rsc_class == None or rsc_type == None :
			return None
		lines = self.query("rsc_metadata\n%s\n%s\n%s"% \
				(rsc_class, rsc_type, rsc_provider),True)
		if lines == None :
			return None
		meta_data = ""
		for line in lines :
			if len(line)!= 0 :
				meta_data = meta_data + line + "\n"
		try :
			doc_xml = parseString(meta_data).documentElement
		except xml.parsers.expat.ExpatError:
			debug("fail to parse the metadata of "+rsc_type)
			return None
			
		meta = RAMeta()
		meta.name = doc_xml.getAttribute("name")
		meta.version = ""
		version_xml = doc_xml.getElementsByTagName("version")
		if version_xml != [] and version_xml[0] in doc_xml.childNodes :
			meta.version = version_xml[0].childNodes[0].data

		meta.longdesc = self.get_locale_desc(doc_xml, "longdesc");
		meta.shortdesc = self.get_locale_desc(doc_xml, "shortdesc");
		
		meta.parameters = []
		for param_xml in doc_xml.getElementsByTagName("parameter") :
			param = {}
			param["name"] = param_xml.getAttribute("name")
			param["required"] = param_xml.getAttribute("required")
			param["unique"] = param_xml.getAttribute("unique")
			param["longdesc"] = self.get_locale_desc(param_xml, "longdesc");
			param["shortdesc"] = self.get_locale_desc(param_xml, "shortdesc");
			content_xml = param_xml.getElementsByTagName("content")[0]
			content = {}
			content["type"] = content_xml.getAttribute("type")
			content["default"] = content_xml.getAttribute("default")
			param["value"] = content["default"]
			param["content"] = content
			meta.parameters.append(param)
		meta.actions = []
		for action_xml in doc_xml.getElementsByTagName("action") :
			action = {}
			for key in action_xml.attributes.keys() :
				action[key] = action_xml.getAttribute(key)
			meta.actions.append(action)
		return meta
	
	def get_rsc_classes(self) :
		return self.query("rsc_classes",True);

	def get_rsc_types(self, rsc_class) :
		return self.query("rsc_types\n"+rsc_class,True)
	
	def get_rsc_providers(self, rsc_class, rsc_type) :
		provider = self.query("rsc_providers\n%s\n%s"%(rsc_class, rsc_type),True)
		if provider == [] :
			return ["heartbeat"]
		return provider

	def rsc_exists(self, rsc_id) :
		return rsc_id in self.get_all_rsc_id()

	def update_attrs(self, up_cmd, del_cmd, old_attrs, new_attrs, keys):
		oldkeys = []
		if old_attrs != None :
			for attr in old_attrs :
				oldkeys.append(attr["id"])
			
		newkeys = []
		if new_attrs != None :
			for attr in new_attrs :
				newkeys.append(attr["id"])
			
		for key in oldkeys :
			if key not in newkeys :
				self.do_cmd(del_cmd+"\n"+key)
				if self.failed_reason != "" :
					msgbox(self.failed_reason)

		if new_attrs != [] :		
			for attr in new_attrs:
				cmd = up_cmd
				for key in keys :
					cmd += "\n" + attr[key]
				self.do_cmd(cmd)
				if self.failed_reason != "" :
					msgbox(self.failed_reason)
	def update_rsc_attr(self, rid, name, value) :
		self.do_cmd("up_rsc_attr\n%s\n%s\n%s"%(rid,name,value))
			
	# clone functions
	def get_clone(self, clone_id) :
		attrs = manager.query("get_clone\n"+clone_id)
		if attrs == None :
			return None
		attrs_name = ["id","clone_max","clone_node_max"]
		clone = dict(zip(attrs_name,attrs))
		return clone
	
	def update_clone(self, clone) :
		cmd = "up_clone\n%s\n%s\n%s"% \
			(clone["id"],clone["clone_max"],clone["clone_node_max"])
		self.do_cmd(cmd)
		if self.failed_reason != "" :
			msgbox(self.failed_reason)
		
	# master functions
	def get_master(self, master_id) :
		attrs = manager.query("get_master\n"+master_id)
		if attrs == None :
			return None
		attrs_name = ["id","clone_max","clone_node_max","master_max","master_node_max"]
		master = dict(zip(attrs_name,attrs))
		return master
	
	def update_master(self, master) :
		cmd = "up_master\n%s\n%s\n%s\n%s\n%s"%(master["id"],
			master["clone_max"],master["clone_node_max"],
			master["master_max"],master["master_node_max"])
		self.do_cmd(cmd)
		if self.failed_reason != "" :
			msgbox(self.failed_reason)
		
	# constraint functions
	def get_constraints(self, type) :
		id_list = manager.query("get_cos\n"+type)
		
		constraints = []
		for id in id_list :
			constraints.append(self.get_constraint(type, id))
							
		return constraints
			
	def get_constraint(self, type, id) :
		if type == "rsc_location" :
			location_attr_names = ["id","rsc","score", "boolean_op"]
			expr_attr_names = ["id","attribute","operation","value"]
			attrs = manager.query("get_co\nrsc_location\n"+id)
			if attrs == None :
				return None
			location = dict(zip(location_attr_names,attrs[:4]))
			location["exprs"] = []
			for i in range((len(attrs)-len(location_attr_names))/len(expr_attr_names)) :
				expr = dict(zip(expr_attr_names,attrs[4+i*4:8+i*4]))
				location["exprs"].append(expr)
			return location
		elif type == "rsc_order" :
			order_attr_names = ["id","from","type","to"]
			attrs = manager.query("get_co\nrsc_order\n" + id)
			if attrs == None :
				return None
			order = dict(zip(order_attr_names,attrs))
			return order
		elif type == "rsc_colocation" :
			colocation_attr_names = ["id","from","to","score"]
			attrs = manager.query("get_co\nrsc_colocation\n" + id)
			if attrs == None :
				return None
			colocation = dict(zip(colocation_attr_names,attrs))
			return colocation
			
				
	def update_constraint(self, type, constraint) :
		if constraint["id"] in manager.get_constraints(type) :
			self.do_cmd("del_co\n"+type+"\n" + constraint["id"])
		if type == "rsc_location" :
			manager.do_cmd("del_co\nrsc_location\n"+constraint["id"])
			if self.failed_reason != "" :
				msgbox(self.failed_reason)
			cmd = "up_co\nrsc_location\n%s\n%s\n%s\n%s"% \
					(constraint["id"],constraint["rsc"],constraint["score"],constraint["boolean_op"])
			for expr in constraint["exprs"] :
				cmd = cmd + "\n" + expr["id"] + "\n" + expr["attribute"] + \
					"\n" + expr["operation"] + "\n" + expr["value"]
		elif type == "rsc_order" :
			cmd = "up_co\nrsc_order\n%s\n%s\n%s\n%s"% \
			 (constraint["id"],constraint["from"],constraint["type"],constraint["to"])
		elif type == "rsc_colocation" :
			cmd = "up_co\nrsc_colocation\n%s\n%s\n%s\n%s"% 	(constraint["id"],constraint["from"],constraint["to"],constraint["score"])
		self.do_cmd(cmd)
		if self.failed_reason != "" :
			msgbox(self.failed_reason)

if __name__ == '__main__' :
	if not pygtk_2_4_newer () :
		print "the pygtk 2.4 or newer is needed."
		sys.exit()
	gc.enable()
	syslog.openlog("haclient", 0, syslog.LOG_USER)
	locale.setlocale(locale.LC_ALL, '')
	gettext.bindtextdomain(app_name)
	gettext.textdomain(app_name)
	gtk.glade.bindtextdomain(app_name)
	gtk.glade.textdomain(app_name)
	gettext.install(app_name,"/usr/share/locale",unicode=1)
	if "-v" in sys.argv :
		debug_level = 1
	manager = Manager()
	window = MainWindow()
	top_window = window.win_widget
	manager.run()
	syslog.closelog()
